mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
initial port complete, untested, builds
This commit is contained in:
@@ -65,6 +65,8 @@ pub enum BarcodeFormat {
|
|||||||
/** QR Code 2D barcode format. */
|
/** QR Code 2D barcode format. */
|
||||||
QR_CODE,
|
QR_CODE,
|
||||||
|
|
||||||
|
MICRO_QR_CODE,
|
||||||
|
|
||||||
/** RSS 14 */
|
/** RSS 14 */
|
||||||
RSS_14,
|
RSS_14,
|
||||||
|
|
||||||
@@ -102,6 +104,7 @@ impl Display for BarcodeFormat {
|
|||||||
BarcodeFormat::MAXICODE => "maxicode",
|
BarcodeFormat::MAXICODE => "maxicode",
|
||||||
BarcodeFormat::PDF_417 => "pdf 417",
|
BarcodeFormat::PDF_417 => "pdf 417",
|
||||||
BarcodeFormat::QR_CODE => "qrcode",
|
BarcodeFormat::QR_CODE => "qrcode",
|
||||||
|
BarcodeFormat::MICRO_QR_CODE => "mqr",
|
||||||
BarcodeFormat::RSS_14 => "rss 14",
|
BarcodeFormat::RSS_14 => "rss 14",
|
||||||
BarcodeFormat::RSS_EXPANDED => "rss expanded",
|
BarcodeFormat::RSS_EXPANDED => "rss expanded",
|
||||||
BarcodeFormat::UPC_A => "upc a",
|
BarcodeFormat::UPC_A => "upc a",
|
||||||
@@ -140,6 +143,9 @@ impl From<&str> for BarcodeFormat {
|
|||||||
"maxicode" | "maxi_code" => BarcodeFormat::MAXICODE,
|
"maxicode" | "maxi_code" => BarcodeFormat::MAXICODE,
|
||||||
"pdf 417" | "pdf_417" | "pdf417" | "iso 15438" | "iso_15438" => BarcodeFormat::PDF_417,
|
"pdf 417" | "pdf_417" | "pdf417" | "iso 15438" | "iso_15438" => BarcodeFormat::PDF_417,
|
||||||
"qrcode" | "qr_code" | "qr code" => BarcodeFormat::QR_CODE,
|
"qrcode" | "qr_code" | "qr code" => BarcodeFormat::QR_CODE,
|
||||||
|
"mqr" | "microqr" | "micro_qr" | "micro_qrcode" | "micro_qr_code" | "mqr_code" => {
|
||||||
|
BarcodeFormat::MICRO_QR_CODE
|
||||||
|
}
|
||||||
"rss 14" | "rss_14" | "rss14" | "gs1 databar" | "gs1 databar coupon"
|
"rss 14" | "rss_14" | "rss14" | "gs1 databar" | "gs1 databar coupon"
|
||||||
| "gs1_databar_coupon" => BarcodeFormat::RSS_14,
|
| "gs1_databar_coupon" => BarcodeFormat::RSS_14,
|
||||||
"rss expanded" | "expanded rss" | "rss_expanded" => BarcodeFormat::RSS_EXPANDED,
|
"rss expanded" | "expanded rss" | "rss_expanded" => BarcodeFormat::RSS_EXPANDED,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use crate::common::Result;
|
use crate::common::Result;
|
||||||
|
use crate::qrcode::cpp_port::QrReader;
|
||||||
use crate::{
|
use crate::{
|
||||||
aztec::AztecReader, datamatrix::DataMatrixReader, maxicode::MaxiCodeReader,
|
aztec::AztecReader, datamatrix::DataMatrixReader, maxicode::MaxiCodeReader,
|
||||||
oned::MultiFormatOneDReader, pdf417::PDF417Reader, qrcode::QRCodeReader, BarcodeFormat,
|
oned::MultiFormatOneDReader, pdf417::PDF417Reader, qrcode::QRCodeReader, BarcodeFormat,
|
||||||
@@ -164,7 +165,16 @@ impl MultiFormatReader {
|
|||||||
for possible_format in self.possible_formats.iter() {
|
for possible_format in self.possible_formats.iter() {
|
||||||
let res = match possible_format {
|
let res = match possible_format {
|
||||||
BarcodeFormat::QR_CODE => {
|
BarcodeFormat::QR_CODE => {
|
||||||
QRCodeReader::default().decode_with_hints(image, &self.hints)
|
let default_qr =
|
||||||
|
QRCodeReader::default().decode_with_hints(image, &self.hints);
|
||||||
|
if default_qr.is_ok() {
|
||||||
|
default_qr
|
||||||
|
} else {
|
||||||
|
QrReader::default().decode_with_hints(image, &self.hints)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BarcodeFormat::MICRO_QR_CODE => {
|
||||||
|
QrReader::default().decode_with_hints(image, &self.hints)
|
||||||
}
|
}
|
||||||
BarcodeFormat::DATA_MATRIX => {
|
BarcodeFormat::DATA_MATRIX => {
|
||||||
DataMatrixReader::default().decode_with_hints(image, &self.hints)
|
DataMatrixReader::default().decode_with_hints(image, &self.hints)
|
||||||
@@ -199,6 +209,9 @@ impl MultiFormatReader {
|
|||||||
if let Ok(res) = QRCodeReader::default().decode_with_hints(image, &self.hints) {
|
if let Ok(res) = QRCodeReader::default().decode_with_hints(image, &self.hints) {
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
if let Ok(res) = QrReader::default().decode_with_hints(image, &self.hints) {
|
||||||
|
return Ok(res);
|
||||||
|
}
|
||||||
if let Ok(res) = DataMatrixReader::default().decode_with_hints(image, &self.hints) {
|
if let Ok(res) = DataMatrixReader::default().decode_with_hints(image, &self.hints) {
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ use crate::{
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
|
||||||
pub struct FinderPatternSet {
|
pub struct FinderPatternSet {
|
||||||
bl: ConcentricPattern,
|
pub bl: ConcentricPattern,
|
||||||
tl: ConcentricPattern,
|
pub tl: ConcentricPattern,
|
||||||
tr: ConcentricPattern,
|
pub tr: ConcentricPattern,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FinderPatterns = Vec<ConcentricPattern>;
|
pub type FinderPatterns = Vec<ConcentricPattern>;
|
||||||
|
|||||||
@@ -5,22 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// #include "QRReader.h"
|
|
||||||
|
|
||||||
// #include "BinaryBitmap.h"
|
|
||||||
// #include "ConcentricFinder.h"
|
|
||||||
// #include "DecodeHints.h"
|
|
||||||
// #include "DecoderResult.h"
|
|
||||||
// #include "DetectorResult.h"
|
|
||||||
// #include "LogMatrix.h"
|
|
||||||
// #include "QRDecoder.h"
|
|
||||||
// #include "QRDetector.h"
|
|
||||||
// #include "Result.h"
|
|
||||||
|
|
||||||
// #include <utility>
|
|
||||||
|
|
||||||
// namespace ZXing::QRCode {
|
|
||||||
|
|
||||||
// Result Reader::decode(const BinaryBitmap& image) const
|
// Result Reader::decode(const BinaryBitmap& image) const
|
||||||
// {
|
// {
|
||||||
// #if 1
|
// #if 1
|
||||||
@@ -133,13 +117,31 @@
|
|||||||
|
|
||||||
// } // namespace ZXing::QRCode
|
// } // namespace ZXing::QRCode
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
common::{cpp_essentials::ConcentricPattern, DetectorRXingResult, HybridBinarizer},
|
||||||
|
multi::MultipleBarcodeReader,
|
||||||
|
BarcodeFormat, BinaryBitmap, DecodeHintType, DecodeHintValue, DecodingHintDictionary,
|
||||||
|
Exceptions, RXingResult, Reader,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{Reader, DecodingHintDictionary, multi::MultipleBarcodeReader};
|
use super::{
|
||||||
|
decoder::Decode,
|
||||||
|
detector::{
|
||||||
|
DetectPureMQR, DetectPureQR, FindFinderPatterns, GenerateFinderPatternSets, SampleMQR,
|
||||||
|
SampleQR,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct QrReader{}
|
use crate::qrcode::detector::QRCodeDetectorResult as DetectorResult;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct QrReader;
|
||||||
|
|
||||||
impl Reader for QrReader {
|
impl Reader for QrReader {
|
||||||
fn decode<B: crate::Binarizer>(&mut self, image: &mut crate::BinaryBitmap<B>) -> crate::common::Result<crate::RXingResult> {
|
fn decode<B: crate::Binarizer>(
|
||||||
|
&mut self,
|
||||||
|
image: &mut crate::BinaryBitmap<B>,
|
||||||
|
) -> crate::common::Result<crate::RXingResult> {
|
||||||
self.decode_with_hints(image, &DecodingHintDictionary::new())
|
self.decode_with_hints(image, &DecodingHintDictionary::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,47 +149,67 @@ impl Reader for QrReader {
|
|||||||
&mut self,
|
&mut self,
|
||||||
image: &mut crate::BinaryBitmap<B>,
|
image: &mut crate::BinaryBitmap<B>,
|
||||||
hints: &crate::DecodingHintDictionary,
|
hints: &crate::DecodingHintDictionary,
|
||||||
) -> crate::common::Result<crate::RXingResult> {
|
) -> crate::common::Result<RXingResult> {
|
||||||
todo!()
|
|
||||||
// #if 1
|
// #if 1
|
||||||
|
if !matches!(
|
||||||
|
hints.get(&DecodeHintType::PURE_BARCODE),
|
||||||
|
Some(DecodeHintValue::PureBarcode(true))
|
||||||
|
)
|
||||||
|
// if !matches!(Some(hints.get(&DecodeHintType::PURE_BARCODE)))
|
||||||
// if (!_hints.isPure())
|
// if (!_hints.isPure())
|
||||||
|
{
|
||||||
|
return Ok(self
|
||||||
|
.decode_set_number_with_hints(image, hints, 1)?
|
||||||
|
.first()
|
||||||
|
.ok_or(Exceptions::NOT_FOUND)?
|
||||||
|
.clone());
|
||||||
// return FirstOrDefault(decode(image, 1));
|
// return FirstOrDefault(decode(image, 1));
|
||||||
|
}
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// auto binImg = image.getBitMatrix();
|
let binImg = image.get_black_matrix(); //image.getBitMatrix();
|
||||||
// if (binImg == nullptr)
|
// if (binImg == nullptr)
|
||||||
// return {};
|
// {return {};}
|
||||||
|
|
||||||
// DetectorResult detectorResult;
|
let mut detectorResult = Err(Exceptions::NOT_FOUND);
|
||||||
// if (_hints.hasFormat(BarcodeFormat::QRCode))
|
if let Some(DecodeHintValue::PossibleFormats(formats)) =
|
||||||
// detectorResult = DetectPureQR(*binImg);
|
hints.get(&DecodeHintType::POSSIBLE_FORMATS)
|
||||||
// if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !detectorResult.isValid())
|
{
|
||||||
// detectorResult = DetectPureMQR(*binImg);
|
if formats.contains(&BarcodeFormat::QR_CODE) {
|
||||||
|
detectorResult = DetectPureQR(binImg);
|
||||||
|
}
|
||||||
|
if formats.contains(&BarcodeFormat::MICRO_QR_CODE) && detectorResult.is_err() {
|
||||||
|
detectorResult = DetectPureMQR(binImg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let detectorResult = detectorResult?;
|
||||||
|
|
||||||
|
// let detectorResult: DetectorResult;
|
||||||
|
// if (_hints.hasFormat(BarcodeFormat::QR_CODE))
|
||||||
|
// {detectorResult = DetectPureQR(binImg);}
|
||||||
|
|
||||||
|
// if (_hints.hasFormat(BarcodeFormat::MICRO_QR_CODE) && !detectorResult.isValid())
|
||||||
|
// {detectorResult = DetectPureMQR(binImg);}
|
||||||
|
|
||||||
// if (!detectorResult.isValid())
|
// if (!detectorResult.isValid())
|
||||||
// return {};
|
// {return {};}
|
||||||
|
|
||||||
// auto decoderResult = Decode(detectorResult.bits());
|
let decoderResult = Decode(detectorResult.getBits())?;
|
||||||
// auto position = detectorResult.position();
|
let position = detectorResult.getPoints();
|
||||||
|
|
||||||
|
Ok(RXingResult::new(
|
||||||
|
&decoderResult.content().to_string(),
|
||||||
|
decoderResult.content().bytes().to_vec(),
|
||||||
|
position.to_vec(),
|
||||||
|
if detectorResult.getBits().width() < 21 {
|
||||||
|
BarcodeFormat::MICRO_QR_CODE
|
||||||
|
} else {
|
||||||
|
BarcodeFormat::QR_CODE
|
||||||
|
},
|
||||||
|
))
|
||||||
// return Result(std::move(decoderResult), std::move(position),
|
// return Result(std::move(decoderResult), std::move(position),
|
||||||
// detectorResult.bits().width() < 21 ? BarcodeFormat::MicroQRCode : BarcodeFormat::QRCode);
|
// detectorResult.bits().width() < 21 ? BarcodeFormat::MICRO_QR_CODE : BarcodeFormat::QR_CODE);
|
||||||
// }
|
|
||||||
|
|
||||||
// void logFPSet(const FinderPatternSet& fps [[maybe_unused]])
|
|
||||||
// {
|
|
||||||
// #ifdef PRINT_DEBUG
|
|
||||||
// auto drawLine = [](PointF a, PointF b) {
|
|
||||||
// int steps = maxAbsComponent(b - a);
|
|
||||||
// PointF dir = bresenhamDirection(PointF(b - a));
|
|
||||||
// for (int i = 0; i < steps; ++i)
|
|
||||||
// log(centered(a + i * dir), 2);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// drawLine(fps.bl, fps.tl);
|
|
||||||
// drawLine(fps.tl, fps.tr);
|
|
||||||
// drawLine(fps.tr, fps.bl);
|
|
||||||
// #endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,69 +226,125 @@ impl MultipleBarcodeReader for QrReader {
|
|||||||
image: &mut crate::BinaryBitmap<B>,
|
image: &mut crate::BinaryBitmap<B>,
|
||||||
hints: &DecodingHintDictionary,
|
hints: &DecodingHintDictionary,
|
||||||
) -> crate::common::Result<Vec<crate::RXingResult>> {
|
) -> crate::common::Result<Vec<crate::RXingResult>> {
|
||||||
todo!()
|
self.decode_set_number_with_hints(image, hints, u32::MAX)
|
||||||
// auto binImg = image.getBitMatrix();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QrReader {
|
||||||
|
fn decode_set_number_with_hints<B: crate::Binarizer>(
|
||||||
|
&mut self,
|
||||||
|
image: &mut crate::BinaryBitmap<B>,
|
||||||
|
hints: &DecodingHintDictionary,
|
||||||
|
count: u32,
|
||||||
|
) -> crate::common::Result<Vec<RXingResult>> {
|
||||||
|
let binImg = image.get_black_matrix(); //image.getBitMatrix();
|
||||||
|
let maxSymbols = count;
|
||||||
// if (binImg == nullptr)
|
// if (binImg == nullptr)
|
||||||
// return {};
|
// {return {};}
|
||||||
|
|
||||||
// #ifdef PRINT_DEBUG
|
// #ifdef PRINT_DEBUG
|
||||||
// LogMatrixWriter lmw(log, *binImg, 5, "qr-log.pnm");
|
// LogMatrixWriter lmw(log, *binImg, 5, "qr-log.pnm");
|
||||||
// #endif
|
// #endif
|
||||||
|
let try_harder = matches!(
|
||||||
|
hints.get(&DecodeHintType::TRY_HARDER),
|
||||||
|
Some(DecodeHintValue::TryHarder(true))
|
||||||
|
);
|
||||||
|
|
||||||
// auto allFPs = FindFinderPatterns(*binImg, _hints.tryHarder());
|
let mut allFPs = FindFinderPatterns(binImg, try_harder);
|
||||||
|
|
||||||
// #ifdef PRINT_DEBUG
|
// #ifdef PRINT_DEBUG
|
||||||
// printf("allFPs: %d\n", Size(allFPs));
|
// printf("allFPs: %d\n", Size(allFPs));
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// std::vector<ConcentricPattern> usedFPs;
|
let mut usedFPs: Vec<ConcentricPattern> = Vec::new();
|
||||||
// Results results;
|
let mut results: Vec<RXingResult> = Vec::new();
|
||||||
|
|
||||||
|
let (check_qr, check_mqr) = if let Some(DecodeHintValue::PossibleFormats(formats)) =
|
||||||
|
hints.get(&DecodeHintType::POSSIBLE_FORMATS)
|
||||||
|
{
|
||||||
|
(
|
||||||
|
formats.contains(&BarcodeFormat::QR_CODE),
|
||||||
|
formats.contains(&BarcodeFormat::MICRO_QR_CODE),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(true, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
if check_qr {
|
||||||
// if (_hints.hasFormat(BarcodeFormat::QRCode)) {
|
// if (_hints.hasFormat(BarcodeFormat::QRCode)) {
|
||||||
// auto allFPSets = GenerateFinderPatternSets(allFPs);
|
let allFPSets = GenerateFinderPatternSets(&mut allFPs);
|
||||||
|
for fpSet in allFPSets {
|
||||||
// for (const auto& fpSet : allFPSets) {
|
// for (const auto& fpSet : allFPSets) {
|
||||||
// if (Contains(usedFPs, fpSet.bl) || Contains(usedFPs, fpSet.tl) || Contains(usedFPs, fpSet.tr))
|
if (usedFPs.contains(&fpSet.bl)
|
||||||
// continue;
|
|| usedFPs.contains(&fpSet.tl)
|
||||||
|
|| usedFPs.contains(&fpSet.tr))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// logFPSet(fpSet);
|
// logFPSet(fpSet);
|
||||||
|
|
||||||
// auto detectorResult = SampleQR(*binImg, fpSet);
|
let detectorResult = SampleQR(binImg, &fpSet);
|
||||||
// if (detectorResult.isValid()) {
|
if let Ok(detectorResult) = detectorResult {
|
||||||
// auto decoderResult = Decode(detectorResult.bits());
|
// if (detectorResult.is_ok()) {
|
||||||
// auto position = detectorResult.position();
|
let decoderResult = Decode(detectorResult.getBits());
|
||||||
// if (decoderResult.isValid()) {
|
let position = detectorResult.getPoints();
|
||||||
// usedFPs.push_back(fpSet.bl);
|
if let Ok(decoderResult) = decoderResult {
|
||||||
// usedFPs.push_back(fpSet.tl);
|
if (decoderResult.isValid()) {
|
||||||
// usedFPs.push_back(fpSet.tr);
|
usedFPs.push(fpSet.bl);
|
||||||
// }
|
usedFPs.push(fpSet.tl);
|
||||||
// if (decoderResult.isValid(_hints.returnErrors())) {
|
usedFPs.push(fpSet.tr);
|
||||||
// results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QRCode);
|
}
|
||||||
// if (maxSymbols && Size(results) == maxSymbols)
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
if (decoderResult.isValid()) {
|
||||||
|
results.push(RXingResult::new(
|
||||||
|
&decoderResult.content().to_string(),
|
||||||
|
decoderResult.content().bytes().to_vec(),
|
||||||
|
position.to_vec(),
|
||||||
|
BarcodeFormat::QR_CODE,
|
||||||
|
));
|
||||||
|
// results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::QR_CODE);
|
||||||
|
|
||||||
|
if (maxSymbols != 0 && (results.len() as u32) == maxSymbols) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (check_mqr && !(maxSymbols != 0 && (results.len() as u32) == maxSymbols)) {
|
||||||
// if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !(maxSymbols && Size(results) == maxSymbols)) {
|
// if (_hints.hasFormat(BarcodeFormat::MicroQRCode) && !(maxSymbols && Size(results) == maxSymbols)) {
|
||||||
|
for fp in allFPs {
|
||||||
// for (const auto& fp : allFPs) {
|
// for (const auto& fp : allFPs) {
|
||||||
// if (Contains(usedFPs, fp))
|
if (usedFPs.contains(&fp)) {
|
||||||
// continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// auto detectorResult = SampleMQR(*binImg, fp);
|
let detectorResult = SampleMQR(binImg, fp);
|
||||||
// if (detectorResult.isValid()) {
|
if let Ok(detectorResult) = detectorResult {
|
||||||
// auto decoderResult = Decode(detectorResult.bits());
|
// if (detectorResult.is_ok()) {
|
||||||
// auto position = detectorResult.position();
|
let decoderResult = Decode(detectorResult.getBits());
|
||||||
// if (decoderResult.isValid(_hints.returnErrors())) {
|
let position = detectorResult.getPoints();
|
||||||
// results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MicroQRCode);
|
if let Ok(decoderResult) = decoderResult {
|
||||||
// if (maxSymbols && Size(results) == maxSymbols)
|
if (decoderResult.isValid()) {
|
||||||
// break;
|
results.push(RXingResult::new(
|
||||||
// }
|
&decoderResult.content().to_string(),
|
||||||
|
decoderResult.content().bytes().to_vec(),
|
||||||
|
position.to_vec(),
|
||||||
|
BarcodeFormat::MICRO_QR_CODE,
|
||||||
|
));
|
||||||
|
// results.emplace_back(std::move(decoderResult), std::move(position), BarcodeFormat::MICRO_QR_CODE);
|
||||||
|
|
||||||
// }
|
if (maxSymbols != 0 && (results.len() as u32) == maxSymbols) {
|
||||||
// }
|
break;
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
// return results;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(results);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ pub use qr_code_reader::*;
|
|||||||
mod qr_code_writer;
|
mod qr_code_writer;
|
||||||
pub use qr_code_writer::*;
|
pub use qr_code_writer::*;
|
||||||
|
|
||||||
mod cpp_port;
|
pub mod cpp_port;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
|
|||||||
Reference in New Issue
Block a user