continued progress on aztec, no pass

This commit is contained in:
Henry Schimke
2022-09-23 17:09:46 -05:00
parent 96d42c23a6
commit fb08ee0e34
19 changed files with 2379 additions and 1624 deletions

View File

@@ -1,123 +0,0 @@
/*
* Copyright 2010 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.aztec;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.Reader;
import com.google.zxing.RXingResult;
import com.google.zxing.RXingResultMetadataType;
import com.google.zxing.RXingResultPoint;
import com.google.zxing.RXingResultPointCallback;
import com.google.zxing.aztec.decoder.Decoder;
import com.google.zxing.aztec.detector.Detector;
import com.google.zxing.common.DecoderRXingResult;
import java.util.List;
import java.util.Map;
/**
* This implementation can detect and decode Aztec codes in an image.
*
* @author David Olivier
*/
public final class AztecReader implements Reader {
/**
* Locates and decodes a Data Matrix code in an image.
*
* @return a String representing the content encoded by the Data Matrix code
* @throws NotFoundException if a Data Matrix code cannot be found
* @throws FormatException if a Data Matrix code cannot be decoded
*/
@Override
public RXingResult decode(BinaryBitmap image) throws NotFoundException, FormatException {
return decode(image, null);
}
@Override
public RXingResult decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
throws NotFoundException, FormatException {
NotFoundException notFoundException = null;
FormatException formatException = null;
Detector detector = new Detector(image.getBlackMatrix());
RXingResultPoint[] points = null;
DecoderRXingResult decoderRXingResult = null;
try {
AztecDetectorRXingResult detectorRXingResult = detector.detect(false);
points = detectorRXingResult.getPoints();
decoderRXingResult = new Decoder().decode(detectorRXingResult);
} catch (NotFoundException e) {
notFoundException = e;
} catch (FormatException e) {
formatException = e;
}
if (decoderRXingResult == null) {
try {
AztecDetectorRXingResult detectorRXingResult = detector.detect(true);
points = detectorRXingResult.getPoints();
decoderRXingResult = new Decoder().decode(detectorRXingResult);
} catch (NotFoundException | FormatException e) {
if (notFoundException != null) {
throw notFoundException;
}
if (formatException != null) {
throw formatException;
}
throw e;
}
}
if (hints != null) {
RXingResultPointCallback rpcb = (RXingResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
if (rpcb != null) {
for (RXingResultPoint point : points) {
rpcb.foundPossibleRXingResultPoint(point);
}
}
}
RXingResult result = new RXingResult(decoderRXingResult.getText(),
decoderRXingResult.getRawBytes(),
decoderRXingResult.getNumBits(),
points,
BarcodeFormat.AZTEC,
System.currentTimeMillis());
List<byte[]> 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);
}
result.putMetadata(RXingResultMetadataType.SYMBOLOGY_IDENTIFIER, "]z" + decoderRXingResult.getSymbologyModifier());
return result;
}
@Override
public void reset() {
// do nothing
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2013 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.aztec;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.Writer;
import com.google.zxing.aztec.encoder.AztecCode;
import com.google.zxing.aztec.encoder.Encoder;
import com.google.zxing.common.BitMatrix;
import java.nio.charset.Charset;
import java.util.Map;
/**
* Renders an Aztec code as a {@link BitMatrix}.
*/
public final class AztecWriter implements Writer {
@Override
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) {
return encode(contents, format, width, height, null);
}
@Override
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints) {
Charset charset = null; // Do not add any ECI code by default
int eccPercent = Encoder.DEFAULT_EC_PERCENT;
int layers = Encoder.DEFAULT_AZTEC_LAYERS;
if (hints != null) {
if (hints.containsKey(EncodeHintType.CHARACTER_SET)) {
charset = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString());
}
if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
eccPercent = Integer.parseInt(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
}
if (hints.containsKey(EncodeHintType.AZTEC_LAYERS)) {
layers = Integer.parseInt(hints.get(EncodeHintType.AZTEC_LAYERS).toString());
}
}
return encode(contents, format, width, height, charset, eccPercent, layers);
}
private static BitMatrix encode(String contents, BarcodeFormat format,
int width, int height,
Charset charset, int eccPercent, int layers) {
if (format != BarcodeFormat.AZTEC) {
throw new IllegalArgumentException("Can only encode AZTEC, but got " + format);
}
AztecCode aztec = Encoder.encode(contents, eccPercent, layers, charset);
return renderRXingResult(aztec, width, height);
}
private static BitMatrix renderRXingResult(AztecCode code, int width, int height) {
BitMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
int outputWidth = Math.max(width, inputWidth);
int outputHeight = Math.max(height, inputHeight);
int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
// Write the contents of this row of the barcode
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (input.get(inputX, inputY)) {
output.setRegion(outputX, outputY, multiple, multiple);
}
}
}
return output;
}
}

View File

@@ -34,7 +34,12 @@
// import java.util.Random;
// import java.util.TreeSet;
use crate::common::BitMatrix;
use crate::{aztec::decoder, common::BitMatrix, exceptions::Exceptions};
use super::{
detector::{self, Detector, Point},
encoder::{self, AztecCode},
};
/**
* Tests for the Detector
@@ -42,147 +47,198 @@ use crate::common::BitMatrix;
* @author Frank Yellin
*/
#[test]
fn testErrorInParameterLocatorZeroZero() {
#[test]
fn testErrorInParameterLocatorZeroZero() {
// Layers=1, CodeWords=1. So the parameter info and its Reed-Solomon info
// will be completely zero!
testErrorInParameterLocator("X");
}
}
#[test]
fn testErrorInParameterLocatorCompact() {
#[test]
fn testErrorInParameterLocatorCompact() {
testErrorInParameterLocator("This is an example Aztec symbol for Wikipedia.");
}
}
#[test]
fn testErrorInParameterLocatorNotCompact() {
#[test]
fn testErrorInParameterLocatorNotCompact() {
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz";
testErrorInParameterLocator(&format!("{}{}{}",alphabet , alphabet , alphabet));
}
testErrorInParameterLocator(&format!("{}{}{}", alphabet, alphabet, alphabet));
}
// Test that we can tolerate errors in the parameter locator bits
fn testErrorInParameterLocator( data:&str) {
let aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS);
let random = new Random(aztec.getMatrix().hashCode()); // pseudo-random, but deterministic
// Test that we can tolerate errors in the parameter locator bits
fn testErrorInParameterLocator(data: &str) {
let aztec = encoder::encoder::encode(data, 25, encoder::encoder::DEFAULT_AZTEC_LAYERS)
.expect("encode should create");
let random = rand::thread_rng(); //Random(aztec.getMatrix().hashCode()); // pseudo-random, but deterministic
let layers = aztec.getLayers();
let compact = aztec.isCompact();
let orientationPoints = getOrientationPoints(aztec);
for (boolean isMirror : new boolean[] { false, true }) {
for (BitMatrix matrix : getRotations(aztec.getMatrix())) {
// Systematically try every possible 1- and 2-bit error.
for (int error1 = 0; error1 < orientationPoints.size(); error1++) {
for (int error2 = error1; error2 < orientationPoints.size(); error2++) {
BitMatrix copy = isMirror ? transpose(matrix) : clone(matrix);
copy.flip(orientationPoints.get(error1).getX(), orientationPoints.get(error1).getY());
if (error2 > error1) {
// if error2 == error1, we only test a single error
copy.flip(orientationPoints.get(error2).getX(), orientationPoints.get(error2).getY());
let orientationPoints = getOrientationPoints(&aztec);
for isMirror in [false, true] {
// for (boolean isMirror : new boolean[] { false, true }) {
for matrix in getRotations(aztec.getMatrix()) {
// for (BitMatrix matrix : getRotations(aztec.getMatrix())) {
// Systematically try every possible 1- and 2-bit error.
for error1 in 0..orientationPoints.size() {
// for (int error1 = 0; error1 < orientationPoints.size(); error1++) {
for error2 in error1..orientationPoints.size() {
// for (int error2 = error1; error2 < orientationPoints.size(); error2++) {
let copy = if isMirror {
transpose(&matrix)
} else {
clone(&matrix)
};
copy.flip(
orientationPoints.get(error1).getX(),
orientationPoints.get(error1).getY(),
);
if error2 > error1 {
// if error2 == error1, we only test a single error
copy.flip(
orientationPoints.get(error2).getX(),
orientationPoints.get(error2).getY(),
);
}
// The detector doesn't seem to work when matrix bits are only 1x1. So magnify.
let r = Detector::new(makeLarger(&copy, 3)).detect(isMirror);
assert!(r.is_ok());
let r = r.expect("result already tested as ok");
assert_eq!(r.getNbLayers(), layers);
assert_eq!(r.isCompact(), compact);
let res = decoder::decode(&r).expect("decode should be ok");
assert_eq!(data, res.getText());
}
}
// Try a few random three-bit errors;
for i in 0..5 {
// for (int i = 0; i < 5; i++) {
let copy = clone(&matrix);
let errors = Vec::new();
while errors.size() < 3 {
// Quick and dirty way of getting three distinct integers between 1 and n.
errors.push(random.nextInt(orientationPoints.size()));
}
for error in errors {
// for (int error : errors) {
copy.flip(
orientationPoints.get(error).getX(),
orientationPoints.get(error).getY(),
);
}
// try {
if let Err(res) = detector::Detector::new(makeLarger(&copy, 3)).detect(false) {
if let Exceptions::NotFoundException(msg) = res {
// all ok
} else {
panic!("Should not reach here");
}
} else {
panic!("Should not reach here");
}
// // new Detector(makeLarger(copy, 3)).detect(false);
// fail("Should not reach here");
// } catch (NotFoundException expected) {
// // continue
// }
}
// The detector doesn't seem to work when matrix bits are only 1x1. So magnify.
AztecDetectorRXingResult r = new Detector(makeLarger(copy, 3)).detect(isMirror);
assertNotNull(r);
assertEquals(r.getNbLayers(), layers);
assertEquals(r.isCompact(), compact);
DecoderRXingResult res = new Decoder().decode(r);
assertEquals(data, res.getText());
}
}
// Try a few random three-bit errors;
for (int i = 0; i < 5; i++) {
BitMatrix copy = clone(matrix);
Collection<Integer> errors = new TreeSet<>();
while (errors.size() < 3) {
// Quick and dirty way of getting three distinct integers between 1 and n.
errors.add(random.nextInt(orientationPoints.size()));
}
for (int error : errors) {
copy.flip(orientationPoints.get(error).getX(), orientationPoints.get(error).getY());
}
try {
new Detector(makeLarger(copy, 3)).detect(false);
fail("Should not reach here");
} catch (NotFoundException expected) {
// continue
}
}
}
}
}
}
// Zooms a bit matrix so that each bit is factor x factor
fn makeLarger( input:&BitMatrix, factor:u32) -> BitMatrix{
// Zooms a bit matrix so that each bit is factor x factor
fn makeLarger(input: &BitMatrix, factor: u32) -> BitMatrix {
let width = input.getWidth();
let output = BitMatrix::new(width * factor);
for (int inputY = 0; inputY < width; inputY++) {
for (int inputX = 0; inputX < width; inputX++) {
if (input.get(inputX, inputY)) {
output.setRegion(inputX * factor, inputY * factor, factor, factor);
let output = BitMatrix::with_single_dimension(width * factor);
for inputY in 0..width {
// for (int inputY = 0; inputY < width; inputY++) {
for inputX in 0..width {
// for (int inputX = 0; inputX < width; inputX++) {
if input.get(inputX, inputY) {
output.setRegion(inputX * factor, inputY * factor, factor, factor);
}
}
}
}
return output;
}
}
// Returns a list of the four rotations of the BitMatrix.
fn getRotations( matrix0:&BitMatrix)-> Vec<BitMatrix> {
// Returns a list of the four rotations of the BitMatrix.
fn getRotations(matrix0: &BitMatrix) -> Vec<BitMatrix> {
let matrix90 = rotateRight(matrix0);
let matrix180 = rotateRight(matrix90);
let matrix270 = rotateRight(matrix180);
return Arrays.asList(matrix0, matrix90, matrix180, matrix270);
}
let matrix180 = rotateRight(&matrix90);
let matrix270 = rotateRight(&matrix180);
vec![*matrix0, matrix90, matrix180, matrix270]
}
// Rotates a square BitMatrix to the right by 90 degrees
fn rotateRight( input:&BitMatrix) -> BitMatrix{
// Rotates a square BitMatrix to the right by 90 degrees
fn rotateRight(input: &BitMatrix) -> BitMatrix {
let width = input.getWidth();
let result = new BitMatrix(width);
for (int x = 0; x < width; x++) {
for (int y = 0; y < width; y++) {
if (input.get(x,y)) {
result.set(y, width - x - 1);
let result = BitMatrix::with_single_dimension(width);
for x in 0..width {
// for (int x = 0; x < width; x++) {
for y in 0..width {
// for (int y = 0; y < width; y++) {
if (input.get(x, y)) {
result.set(y, width - x - 1);
}
}
}
}
return result;
}
}
// Returns the transpose of a bit matrix, which is equivalent to rotating the
// matrix to the right, and then flipping it left-to-right
fn transpose( input:&BitMatrix) -> BitMatrix {
// Returns the transpose of a bit matrix, which is equivalent to rotating the
// matrix to the right, and then flipping it left-to-right
fn transpose(input: &BitMatrix) -> BitMatrix {
let width = input.getWidth();
let result = new BitMatrix(width);
for (int x = 0; x < width; x++) {
for (int y = 0; y < width; y++) {
if (input.get(x, y)) {
result.set(y, x);
let result = BitMatrix::with_single_dimension(width);
for x in 0..width {
// for (int x = 0; x < width; x++) {
for y in 0..width {
// for (int y = 0; y < width; y++) {
if (input.get(x, y)) {
result.set(y, x);
}
}
}
}
return result;
}
}
fn clone( input:&BitMatrix) -> BitMatrix {
fn clone(input: &BitMatrix) -> BitMatrix {
let width = input.getWidth();
let result = new BitMatrix(width);
for (int x = 0; x < width; x++) {
for (int y = 0; y < width; y++) {
if (input.get(x,y)) {
result.set(x,y);
let result = BitMatrix::with_single_dimension(width);
for x in 0..width {
// for (int x = 0; x < width; x++) {
for y in 0..width {
// for (int y = 0; y < width; y++) {
if input.get(x, y) {
result.set(x, y);
}
}
}
}
return result;
}
result
}
fn getOrientationPoints( code:&AztecCode) -> Vec<Point> {
fn getOrientationPoints(code: &AztecCode) -> Vec<Point> {
let center = code.getMatrix().getWidth() / 2;
let offset = code.isCompact() ? 5 : 7;
let result = new ArrayList<>();
for (int xSign = -1; xSign <= 1; xSign += 2) {
for (int ySign = -1; ySign <= 1; ySign += 2) {
result.add(new Point(center + xSign * offset, center + ySign * offset));
result.add(new Point(center + xSign * (offset - 1), center + ySign * offset));
result.add(new Point(center + xSign * offset, center + ySign * (offset - 1)));
}
let offset = if code.isCompact() { 5 } else { 7 };
let result = Vec::new();
let mut xSign = -1;
while xSign <= 1 {
// for (int xSign = -1; xSign <= 1; xSign += 2) {
let mut ySign = -1;
while ySign <= 1 {
// for (int ySign = -1; ySign <= 1; ySign += 2) {
result.add(Point::new(center + xSign * offset, center + ySign * offset));
result.add(Point::new(
center + xSign * (offset - 1),
center + ySign * offset,
));
result.add(Point::new(
center + xSign * offset,
center + ySign * (offset - 1),
));
ySign += 2;
}
xSign += 2;
}
return result;
}
}

View File

@@ -36,7 +36,15 @@
// import java.util.Random;
// import java.util.regex.Pattern;
use crate::common::BitArray;
use std::collections::HashMap;
use encoding::EncodingRef;
use crate::{common::{BitArray, BitMatrix}, RXingResultPoint, BarcodeFormat, aztec::{encoder::HighLevelEncoder, decoder, shared_test_methods::{toBooleanArray, stripSpace, toBitArray}, AztecDetectorResult::AztecDetectorRXingResult}, EncodeHintType,EncodeHintValue};
use super::{AztecWriter, encoder::encoder};
use crate::Writer;
/**
* Aztec 2D generator unit tests.
@@ -45,11 +53,11 @@ use crate::common::BitArray;
* @author Frank Yellin
*/
const ISO_8859_1:&'static encoding::Encoding = StandardCharsets.ISO_8859_1;
const UTF_8 :&'static encoding::Encoding= StandardCharsets.UTF_8;
const SHIFT_JIS:&'static encoding::Encoding = Charset.forName("Shift_JIS");
const ISO_8859_15:&'static encoding::Encoding = Charset.forName("ISO-8859-15");
const WINDOWS_1252 :&'static encoding::Encoding= Charset.forName("Windows-1252");
const ISO_8859_1:EncodingRef = encoding::all::ISO_8859_1;//StandardCharsets.ISO_8859_1;
const UTF_8 :EncodingRef= encoding::all::UTF_8; //StandardCharsets.UTF_8;
const SHIFT_JIS:EncodingRef = encoding::label::encoding_from_whatwg_label("Shift_JIS").expect("must exist");//Charset.forName("Shift_JIS");
const ISO_8859_15:EncodingRef = encoding::all::ISO_8859_15;//Charset.forName("ISO-8859-15");
const WINDOWS_1252 :EncodingRef= encoding::all::WINDOWS_1252;//Charset.forName("Windows-1252");
const DOTX : &str = "[^.X]";
const SPACES :&str= "\\s+";
@@ -60,98 +68,100 @@ use crate::common::BitArray;
#[test]
fn testEncode1() {
testEncode("This is an example Aztec symbol for Wikipedia.", true, 3,
"X X X X X X X X \n" +
"X X X X X X X X X X \n" +
"X X X X X X X X X X X \n" +
"X X X X X X X X X X X \n" +
" X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X \n" +
"X X X X X X X X X X \n" +
" X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X \n" +
" X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X \n" +
" X X X \n" +
" X X X X X X X X X X \n" +
" X X X X X X X X X X \n");
r"X X X X X X X X
X X X X X X X X X X
X X X X X X X X X X X
X X X X X X X X X X X
X X X X X X X X X X X
X X X X X X X X X X X X X
X X X X X X X X X X X X
X X X X X X X X X X X X X X X X
X X X X X X X X X X X
X X X X X X X X X X X X X X X X
X X X X X X X X X X
X X X X X X X X X X
X X X X X X X X X X
X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X
X X X X X X X X X X X X X X X X
X X X X X X X X X X X
X X X X X X X X
X X X X X X X X X X X X X X X X
X X X X X X X X X X X X
X X X
X X X X X X X X X X
X X X X X X X X X X
");
}
#[test]
fn testEncode2() {
testEncode("Aztec Code is a public domain 2D matrix barcode symbology" +
" of nominally square symbols built on a square grid with a " +
"distinctive square bullseye pattern at their center.", false, 6,
" X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X X X X X X X X X \n" +
" X X X X X X X X X X X X X X X X \n" +
"X X X X X X X X X X X X X \n");
testEncode(r"Aztec Code is a public domain 2D matrix barcode symbology
of nominally square symbols built on a square grid with a
distinctive square bullseye pattern at their center.", false, 6,
r" X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X
");
}
fn testAztecWriter() {
testWriter("Espa\u{00F1}ol", null, 25, true, 1); // Without ECI (implicit ISO-8859-1)
testWriter("Espa\u{00F1}ol", ISO_8859_1, 25, true, 1); // Explicit ISO-8859-1
testWriter("\u{20AC} 1 sample data.", WINDOWS_1252, 25, true, 2); // ISO-8859-1 can't encode Euro; Windows-1252 can
testWriter("\u{20AC} 1 sample data.", ISO_8859_15, 25, true, 2);
testWriter("\u{20AC} 1 sample data.", UTF_8, 25, true, 2);
testWriter("\u{20AC} 1 sample data.", UTF_8, 100, true, 3);
testWriter("\u{20AC} 1 sample data.", UTF_8, 300, true, 4);
testWriter("\u{20AC} 1 sample data.", UTF_8, 500, false, 5);
testWriter("The capital of Japan is named \u{6771}\u{4EAC}.", SHIFT_JIS, 25, true, 3);
testWriter("Espa\u{00F1}ol", None, 25, true, 1); // Without ECI (implicit ISO-8859-1)
testWriter("Espa\u{00F1}ol", Some(ISO_8859_1), 25, true, 1); // Explicit ISO-8859-1
testWriter("\u{20AC} 1 sample data.", Some(WINDOWS_1252), 25, true, 2); // ISO-8859-1 can't encode Euro; Windows-1252 can
testWriter("\u{20AC} 1 sample data.", Some(ISO_8859_15), 25, true, 2);
testWriter("\u{20AC} 1 sample data.", Some(UTF_8), 25, true, 2);
testWriter("\u{20AC} 1 sample data.", Some(UTF_8), 100, true, 3);
testWriter("\u{20AC} 1 sample data.", Some(UTF_8), 300, true, 4);
testWriter("\u{20AC} 1 sample data.", Some(UTF_8), 500, false, 5);
testWriter("The capital of Japan is named \u{6771}\u{4EAC}.", Some(SHIFT_JIS), 25, true, 3);
// Test AztecWriter defaults
let data = "In ut magna vel mauris malesuada";
let writer = AztecWriter::new();
let matrix = writer.encode(data, BarcodeFormat.AZTEC, 0, 0);
let aztec = Encoder.encode(data,
Encoder.DEFAULT_EC_PERCENT, Encoder.DEFAULT_AZTEC_LAYERS);
// let writer = AztecWriter{};
let matrix = AztecWriter::encode(data, &BarcodeFormat::AZTEC, 0, 0).expect("matrix must exist");
let aztec = encoder::encode(data,
encoder::DEFAULT_EC_PERCENT, encoder::DEFAULT_AZTEC_LAYERS).expect("encode should succeed");
let expectedMatrix = aztec.getMatrix();
assertEquals(matrix, expectedMatrix);
assert_eq!(&matrix, expectedMatrix);
}
// synthetic tests (encode-decode round-trip)
@@ -178,97 +188,97 @@ use crate::common::BitArray;
#[test]
fn testEncodeDecode5() {
testEncodeDecode("http://test/~!@#*^%&)__ ;:'\"[]{}\\|-+-=`1029384756<>/?abc"
+ "Four score and seven our forefathers brought forth", false, 5);
testEncodeDecode(r#"http://test/~!@#*^%&)__ ;:'"[]{}\|-+-=`1029384756<>/?abc
Four score and seven our forefathers brought forth"#, false, 5);
}
#[test]
fn testEncodeDecode10() {
testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" +
" cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" +
" est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" +
" auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" +
" ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" +
" elementum sapien dolor et diam.", false, 10);
testEncodeDecode(r"In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam
cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum
est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue
auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla
ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id
elementum sapien dolor et diam.", false, 10);
}
#[test]
fn testEncodeDecode23() {
testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" +
" cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" +
" est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" +
" auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" +
" ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" +
" elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend." +
" Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus" +
" justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu" +
" tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus" +
" quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec" +
" laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida," +
" justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec" +
" lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar" +
" nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat" +
" eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra" +
" fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo" +
" diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla" +
" ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum" +
" sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." +
" Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit" +
" felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo" +
" erat pulvinar nisi, id elementum sapien dolor et diam.", false, 23);
testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam
cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum
est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue
auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla
ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id
elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend.
Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus
justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu
tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus
quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec
laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida
justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec
lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar
nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat
eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra
fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo
diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla
ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum
sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet.
Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit
felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo
erat pulvinar nisi, id elementum sapien dolor et diam.", false, 23);
}
#[test]
fn testEncodeDecode31() {
testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" +
" cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" +
" est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" +
" auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" +
" ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" +
" elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend." +
" Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus" +
" justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu" +
" tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus" +
" quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec" +
" laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida," +
" justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec" +
" lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar" +
" nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat" +
" eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra" +
" fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo" +
" diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla" +
" ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum" +
" sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." +
" Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit" +
" felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo" +
" erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit" +
" placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at" +
" pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est." +
" Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada" +
" dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id" +
" justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum" +
" sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat," +
" eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet" +
" laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac" +
" nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula," +
" massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus" +
" sed est. Ut justo diam, lobortis eu tris. In ut magna vel mauris malesuada dictum." +
" Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum" +
" sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." +
" Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget" +
" hendrerit felis turpis nec lorem.", false, 31);
testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam
cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum
est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue
auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla
ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id
elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend.
Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus
justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu
tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus
quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec
laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida,
justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec
lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar
nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat
eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra
fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo
diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla
ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum
sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet.
Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit
felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo
erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit
placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at
pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est.
Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada
dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id
justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum
sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat,
eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet
laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac
nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula,
massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus
sed est. Ut justo diam, lobortis eu tris. In ut magna vel mauris malesuada dictum.
Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum
sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet.
Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget
hendrerit felis turpis nec lorem.", false, 31);
}
#[test]
fn testGenerateModeMessage() {
testModeMessage(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X");
testModeMessage(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX..");
testModeMessage(false, 21, 660, "X.X.. .X.X..X..XX .XXX ..X.. .XXX. .X... ..XXX");
testModeMessage(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX");
testModeMessageComplex(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X");
testModeMessageComplex(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX..");
testModeMessageComplex(false, 21, 660, "X.X.. .X.X..X..XX .XXX ..X.. .XXX. .X... ..XXX");
testModeMessageComplex(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX");
}
#[test]
fn testStuffBits() {
fn testStuffBitsTest() {
testStuffBits(5, ".X.X. X.X.X .X.X.",
".X.X. X.X.X .X.X.");
testStuffBits(5, ".X.X. ..... .X.X",
@@ -308,11 +318,10 @@ use crate::common::BitArray;
//'A' 'B' 'C' B/S =1 'd' 'E' 'F' 'G'
"...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X...");
testHighLevelEncodeString(
testHighLevelEncodeStringCount(
// Found on an airline boarding pass. Several stretches of Binary shift are
// necessary to keep the bitcount so low.
"09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEi"
+ "Ygh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
"09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEiYgh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
823);
}
@@ -345,62 +354,74 @@ use crate::common::BitArray;
// Create a string in which every character requires binary
let sb = String::new();
for (int i = 0; i <= 3000; i++) {
sb.append((char) (128 + (i % 30)));
for i in 0..3000 {
// for (int i = 0; i <= 3000; i++) {
sb.push(char::from_u32(128 + (i % 30)).unwrap());
}
// Test the output generated by Binary/Switch, particularly near the
// places where the encoding changes: 31, 62, and 2047+31=2078
for (int i : new int[] { 1, 2, 3, 10, 29, 30, 31, 32, 33,
60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100 }) {
for i in [1, 2, 3, 10, 29, 30, 31, 32, 33,
60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100]{
// for (int i : new int[] { 1, 2, 3, 10, 29, 30, 31, 32, 33,
// 60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100 }) {
// This is the expected length of a binary string of length "i"
int expectedLength = (8 * i) +
((i <= 31) ? 10 : (i <= 62) ? 20 : (i <= 2078) ? 21 : 31);
let expectedLength = (8 * i) +
if i <= 31 { 10 } else { if i <= 62 {20} else{if i <= 2078 {21} else {31}} };
// ( (i <= 31) ? 10 : (i <= 62) ? 20 : (i <= 2078) ? 21 : 31);
// Verify that we are correct about the length.
testHighLevelEncodeString(sb.substring(0, i), expectedLength);
if (i != 1 && i != 32 && i != 2079) {
testHighLevelEncodeStringCount(&sb[..i], expectedLength as u32);
if i != 1 && i != 32 && i != 2079 {
// The addition of an 'a' at the beginning or end gets merged into the binary code
// in those cases where adding another binary character only adds 8 or 9 bits to the result.
// So we exclude the border cases i=1,32,2079
// A lower case letter at the beginning will be merged into binary mode
testHighLevelEncodeString('a' + sb.substring(0, i - 1), expectedLength);
testHighLevelEncodeStringCount(&format!("a{}" , &sb[.. i - 1]), expectedLength as u32);
// A lower case letter at the end will also be merged into binary mode
testHighLevelEncodeString(sb.substring(0, i - 1) + 'a', expectedLength);
testHighLevelEncodeStringCount(&format!("{}a",&sb[..i - 1]), expectedLength as u32);
}
// A lower case letter at both ends will enough to latch us into LOWER.
testHighLevelEncodeString('a' + sb.substring(0, i) + 'b', expectedLength + 15);
testHighLevelEncodeStringCount(&format!("a{}b",&sb[..i] ), expectedLength as u32+ 15);
}
sb = new StringBuilder();
for (int i = 0; i < 32; i++) {
sb.append('§'); // § forces binary encoding
sb.clear();
for i in 0..32 {
// for (int i = 0; i < 32; i++) {
sb.push('§'); // § forces binary encoding
}
sb.setCharAt(1, 'A');
sb.replace_range(1..2, "A");
// sb.setCharAt(1, 'A');
// expect B/S(1) A B/S(30)
testHighLevelEncodeString(sb.toString(), 5 + 20 + 31 * 8);
testHighLevelEncodeStringCount(&sb, 5 + 20 + 31 * 8);
sb = new StringBuilder();
for (int i = 0; i < 31; i++) {
sb.append('§');
sb.clear();
for i in 0..31 {
// for (int i = 0; i < 31; i++) {
sb.push('§');
}
sb.setCharAt(1, 'A');
sb.replace_range(1..2, "A");
// sb.setCharAt(1, 'A');
// expect B/S(31)
testHighLevelEncodeString(sb.toString(), 10 + 31 * 8);
testHighLevelEncodeStringCount(&sb, 10 + 31 * 8);
sb = new StringBuilder();
for (int i = 0; i < 34; i++) {
sb.append('§');
sb.clear();
for i in 0..34 {
// for (int i = 0; i < 34; i++) {
sb.push('§');
}
sb.setCharAt(1, 'A');
sb.replace_range(1..2, "A");
// sb.setCharAt(1, 'A');
// expect B/S(31) B/S(3)
testHighLevelEncodeString(sb.toString(), 20 + 34 * 8);
testHighLevelEncodeStringCount(&sb, 20 + 34 * 8);
sb = new StringBuilder();
for (int i = 0; i < 64; i++) {
sb.append('§');
sb.clear();
for i in 0..64 {
// for (int i = 0; i < 64; i++) {
sb.push('§');
}
sb.setCharAt(30, 'A');
sb.replace_range(30..31, "A");
// sb.setCharAt(30, 'A');
// expect B/S(64)
testHighLevelEncodeString(sb.toString(), 21 + 64 * 8);
testHighLevelEncodeStringCount(&sb, 21 + 64 * 8);
}
#[test]
@@ -420,7 +441,7 @@ use crate::common::BitArray;
// 'A' D/L '.' ' ' '1' '2' '3' '4'
"...X. XXXX. XX.X ...X ..XX .X.. .X.X .X X.");
// Don't bother leaving Binary Shift.
testHighLevelEncodeString("A\200. \200",
testHighLevelEncodeString("A\u{200}. \u{200}",
// 'A' B/S =2 \200 "." " " \200
"...X. XXXXX ..X.. X....... ..X.XXX. ..X..... X.......");
}
@@ -434,20 +455,20 @@ use crate::common::BitArray;
#[test]
#[should_panic]
fn testUserSpecifiedLayers2() {
doTestUserSpecifiedLayers(-1);
doTestUserSpecifiedLayers(1);
}
fn doTestUserSpecifiedLayers( userSpecifiedLayers:usize) {
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
AztecCode aztec = Encoder.encode(alphabet, 25, -2);
assertEquals(2, aztec.getLayers());
assertTrue(aztec.isCompact());
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let aztec = encoder::encode(alphabet, 25, 2).expect("should encode");
assert_eq!(2, aztec.getLayers());
assert!(aztec.isCompact());
aztec = Encoder.encode(alphabet, 25, 32);
assertEquals(32, aztec.getLayers());
assertFalse(aztec.isCompact());
aztec = encoder::encode(alphabet, 25, 32).expect("should encode");
assert_eq!(32, aztec.getLayers());
assert!(!aztec.isCompact());
Encoder.encode(alphabet, 25, userSpecifiedLayers);
encoder::encode(alphabet, 25, userSpecifiedLayers as u32);
}
#[test]
@@ -455,132 +476,139 @@ use crate::common::BitArray;
fn testBorderCompact4CaseFailed() {
// Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must
// be error correction
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// encodes as 26 * 5 * 4 = 520 bits of data
String alphabet4 = alphabet + alphabet + alphabet + alphabet;
Encoder.encode(alphabet4, 0, -4);
let alphabet4 = format!("{}{}{}{}",alphabet , alphabet, alphabet , alphabet);
encoder::encode(&alphabet4, 0, 4);
}
#[test]
fn testBorderCompact4Case() {
// Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must
// be error correction
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// encodes as 26 * 5 * 4 = 520 bits of data
String alphabet4 = alphabet + alphabet + alphabet + alphabet;
let alphabet4 = format!("{}{}{}{}",alphabet , alphabet, alphabet , alphabet);
// If we just try to encode it normally, it will go to a non-compact 4 layer
AztecCode aztecCode = Encoder.encode(alphabet4, 0, Encoder.DEFAULT_AZTEC_LAYERS);
assertFalse(aztecCode.isCompact());
assertEquals(4, aztecCode.getLayers());
let aztecCode = encoder::encode(&alphabet4, 0, encoder::DEFAULT_AZTEC_LAYERS).expect("Should encode");
assert!(!aztecCode.isCompact());
assert_eq!(4, aztecCode.getLayers());
// But shortening the string to 100 bytes (500 bits of data), compact works fine, even if we
// include more error checking.
aztecCode = Encoder.encode(alphabet4.substring(0, 100), 10, Encoder.DEFAULT_AZTEC_LAYERS);
assertTrue(aztecCode.isCompact());
assertEquals(4, aztecCode.getLayers());
aztecCode = encoder::encode(&alphabet4[..100], 10, encoder::DEFAULT_AZTEC_LAYERS).expect("should encode");
assert!(aztecCode.isCompact());
assert_eq!(4, aztecCode.getLayers());
}
// Helper routines
fn testEncode( data:&str, compact:bool, layers:usize, expected:&str) {
AztecCode aztec = Encoder.encode(data, 33, Encoder.DEFAULT_AZTEC_LAYERS);
assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact());
assertEquals("Unexpected nr. of layers", layers, aztec.getLayers());
BitMatrix matrix = aztec.getMatrix();
assertEquals("encode() failed", expected, matrix.toString());
fn testEncode( data:&str, compact:bool, layers:u32, expected:&str) {
let aztec = encoder::encode(data, 33, encoder::DEFAULT_AZTEC_LAYERS).expect("should encode");
assert_eq!( compact, aztec.isCompact(),"Unexpected symbol format (compact)");
assert_eq!( layers, aztec.getLayers(),"Unexpected nr. of layers");
let matrix = aztec.getMatrix();
assert_eq!( expected, matrix.to_string(),"encode() failed");
let z: BitMatrix;
}
fn testEncodeDecode( data:&str, compact:bool, layers:usize) {
AztecCode aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS);
assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact());
assertEquals("Unexpected nr. of layers", layers, aztec.getLayers());
BitMatrix matrix = aztec.getMatrix();
AztecDetectorRXingResult r =
new AztecDetectorRXingResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
DecoderRXingResult res = new Decoder().decode(r);
assertEquals(data, res.getText());
fn testEncodeDecode( data:&str, compact:bool, layers:u32) {
let aztec = encoder::encode(data, 25, encoder::DEFAULT_AZTEC_LAYERS).expect("should encode");
assert_eq!( compact, aztec.isCompact(),"Unexpected symbol format (compact)");
assert_eq!( layers, aztec.getLayers(),"Unexpected nr. of layers");
let mut matrix = aztec.getMatrix();
let r =
AztecDetectorRXingResult::new(matrix.clone(), NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
let res = decoder::decode(&r).expect("decode ok");
assert_eq!(data, res.getText());
// Check error correction by introducing a few minor errors
Random random = getPseudoRandom();
let random = getPseudoRandom();
matrix.flip(random.nextInt(matrix.getWidth()), random.nextInt(2));
matrix.flip(random.nextInt(matrix.getWidth()), matrix.getHeight() - 2 + random.nextInt(2));
matrix.flip(random.nextInt(2), random.nextInt(matrix.getHeight()));
matrix.flip(matrix.getWidth() - 2 + random.nextInt(2), random.nextInt(matrix.getHeight()));
r = new AztecDetectorRXingResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
res = new Decoder().decode(r);
assertEquals(data, res.getText());
r = AztecDetectorRXingResult::new(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
res = decoder::decode(r);
assert_eq!(data, res.getText());
}
fn testWriter( data:&str,
charset:&'static dyn encoding::Encoding,
charset:Option<EncodingRef>,
eccPercent:u32,
compact:bool,
layers:usize) throws FormatException {
layers:u32) {
// Perform an encode-decode round-trip because it can be lossy.
Map<EncodeHintType,Object> hints = new EnumMap<>(EncodeHintType.class);
if (null != charset) {
hints.put(EncodeHintType.CHARACTER_SET, charset.name());
let hints = HashMap::new();
if charset.is_some() {
hints.insert(EncodingHintType::CHARACTER_SET, EncodingHintValue::CharacterSet(charset.unwrap().name()));
}
hints.put(EncodeHintType.ERROR_CORRECTION, eccPercent);
AztecWriter writer = new AztecWriter();
BitMatrix matrix = writer.encode(data, BarcodeFormat.AZTEC, 0, 0, hints);
AztecCode aztec = Encoder.encode(data, eccPercent,
Encoder.DEFAULT_AZTEC_LAYERS, charset);
assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact());
assertEquals("Unexpected nr. of layers", layers, aztec.getLayers());
BitMatrix matrix2 = aztec.getMatrix();
assertEquals(matrix, matrix2);
AztecDetectorRXingResult r =
new AztecDetectorRXingResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
DecoderRXingResult res = new Decoder().decode(r);
assertEquals(data, res.getText());
// if (null != charset) {
// hints.put(EncodeHintType.CHARACTER_SET, charset.name());
// }
hints.insert(EncodeHintType::ERROR_CORRECTION, eccPercent);
let writer = AztecWriter{};
let matrix = AztecWriter::encode_with_hints(data, &BarcodeFormat::AZTEC, 0, 0, &hints).expect("encoder created");
let aztec = encoder::encode_with_charset(data, eccPercent,
encoder::DEFAULT_AZTEC_LAYERS, charset.unwrap()).expect("encode should encode");
assert_eq!( compact, aztec.isCompact(),"Unexpected symbol format (compact)");
assert_eq!( layers, aztec.getLayers(),"Unexpected nr. of layers");
let matrix2 = aztec.getMatrix();
assert_eq!(&matrix, matrix2);
let r =
AztecDetectorRXingResult::new(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
let res = decoder::decode(&r);
assert_eq!(data, res.getText());
// Check error correction by introducing up to eccPercent/2 errors
int ecWords = aztec.getCodeWords() * eccPercent / 100 / 2;
Random random = getPseudoRandom();
for (int i = 0; i < ecWords; i++) {
let ecWords = aztec.getCodeWords() * eccPercent / 100 / 2;
let random = getPseudoRandom();
for i in 0 ..ecWords {
// for (int i = 0; i < ecWords; i++) {
// don't touch the core
int x = random.nextBoolean() ?
random.nextInt(aztec.getLayers() * 2)
: matrix.getWidth() - 1 - random.nextInt(aztec.getLayers() * 2);
int y = random.nextBoolean() ?
random.nextInt(aztec.getLayers() * 2)
: matrix.getHeight() - 1 - random.nextInt(aztec.getLayers() * 2);
let x = if random.nextBoolean()
{random.nextInt(aztec.getLayers() * 2)}
else {matrix.getWidth() - 1 - random.nextInt(aztec.getLayers() * 2)};
let y = if random.nextBoolean()
{random.nextInt(aztec.getLayers() * 2)}
else {matrix.getHeight() - 1 - random.nextInt(aztec.getLayers() * 2)};
matrix.flip(x, y);
}
r = new AztecDetectorRXingResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
res = new Decoder().decode(r);
assertEquals(data, res.getText());
r = AztecDetectorRXingResult::nwq(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
res = decoder::decode(r);
assert_eq!(data, res.getText());
}
fn getPseudoRandom() -> rand::Rng{
return new Random(0xDEADBEEF);
fn getPseudoRandom() -> rand::rngs::ThreadRng{
rand::thread_rng()
}
fn testModeMessage( compact:bool, layers:usize, words:usize, expected:&str) {
BitArray in = Encoder.generateModeMessage(compact, layers, words);
assertEquals("generateModeMessage() failed", stripSpace(expected), stripSpace(in.toString()));
fn testModeMessageComplex( compact:bool, layers:u32, words:usize, expected:&str) {
let indata = encoder::generateModeMessage(compact, layers, words);
assert_eq!( stripSpace(expected), stripSpace(indata.toString()),"generateModeMessage() failed");
}
fn testStuffBits( wordSize:usize, bits:&str, expected:&str) {
BitArray in = toBitArray(bits);
BitArray stuffed = Encoder.stuffBits(in, wordSize);
assertEquals("stuffBits() failed for input string: " + bits,
stripSpace(expected), stripSpace(stuffed.toString()));
let indata = toBitArray(bits);
let stuffed = encoder::stuffBits(&indata, wordSize);
assert_eq!(
stripSpace(expected), stripSpace(stuffed.toString()),
"stuffBits() failed for input string: {}" , bits);
}
fn testHighLevelEncodeString( s:&str, expectedBits:&str) {
BitArray bits = new HighLevelEncoder(s.getBytes(StandardCharsets.ISO_8859_1)).encode();
String receivedBits = stripSpace(bits.toString());
assertEquals("highLevelEncode() failed for input string: " + s, stripSpace(expectedBits), receivedBits);
assertEquals(s, Decoder.highLevelDecode(toBooleanArray(bits)));
let bits = HighLevelEncoder::new(s.getBytes(StandardCharsets.ISO_8859_1)).encode();
let receivedBits = stripSpace(bits.toString());
assert_eq!( stripSpace(expectedBits), receivedBits,"highLevelEncode() failed for input string: {}" , s);
assert_eq!(s, decoder::highLevelDecode(&toBooleanArray(&bits)));
}
fn testHighLevelEncodeString( s:&str, expectedReceivedBits:u32) {
BitArray bits = new HighLevelEncoder(s.getBytes(StandardCharsets.ISO_8859_1)).encode();
int receivedBitCount = stripSpace(bits.toString()).length();
assertEquals("highLevelEncode() failed for input string: " + s,
expectedReceivedBits, receivedBitCount);
assertEquals(s, Decoder.highLevelDecode(toBooleanArray(bits)));
fn testHighLevelEncodeStringCount( s:&str, expectedReceivedBits:u32) {
let bits = HighLevelEncoder::new(s.getBytes(StandardCharsets.ISO_8859_1)).encode().unwrap();
let receivedBitCount = stripSpace(bits.toString()).len();
assert_eq!(
expectedReceivedBits, receivedBitCount,
"highLevelEncode() failed for input string: {}" , s);
assert_eq!(s, decoder::highLevelDecode(&toBooleanArray(&bits)));
}

123
src/aztec/aztec_reader.rs Normal file
View File

@@ -0,0 +1,123 @@
/*
* Copyright 2010 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::{collections::HashMap, time::UNIX_EPOCH};
use crate::{
common::{DecoderRXingResult, DetectorRXingResult},
exceptions::Exceptions,
BarcodeFormat, BinaryBitmap, DecodeHintType, DecodeHintValue, RXingResult,
RXingResultMetadataType, Reader, RXingResultMetadataValue,
};
use super::{decoder, detector::Detector};
/**
* This implementation can detect and decode Aztec codes in an image.
*
* @author David Olivier
*/
pub struct AztecReader;
impl Reader for AztecReader {
/**
* Locates and decodes a Data Matrix code in an image.
*
* @return a String representing the content encoded by the Data Matrix code
* @throws NotFoundException if a Data Matrix code cannot be found
* @throws FormatException if a Data Matrix code cannot be decoded
*/
fn decode(image: &BinaryBitmap) -> Result<RXingResult, Exceptions> {
Self::decode_with_hints(image, &HashMap::new())
}
fn decode_with_hints(
image: &BinaryBitmap,
hints: &HashMap<DecodeHintType, DecodeHintValue>,
) -> Result<RXingResult, Exceptions> {
// let notFoundException = None;
// let formatException = None;
let mut detector = Detector::new(image.getBlackMatrix()?.clone());
let mut points;
let mut decoderRXingResult: DecoderRXingResult;
// try {
let detectorRXingResult = detector.detect(false)?;
points = detectorRXingResult.getPoints();
decoderRXingResult = decoder::decode(&detectorRXingResult)?;
// } catch (NotFoundException e) {
// notFoundException = e;
// } catch (FormatException e) {
// formatException = e;
// }
// if (decoderRXingResult == null) {
// try {
let detectorRXingResult = detector.detect(true)?;
points = detectorRXingResult.getPoints();
decoderRXingResult = decoder::decode(&detectorRXingResult)?;
// } catch (NotFoundException | FormatException e) {
// if (notFoundException != null) {
// throw notFoundException;
// }
// if (formatException != null) {
// throw formatException;
// }
// throw e;
// }
// }
if let Some(rpcb) = hints.get(&DecodeHintType::NEED_RESULT_POINT_CALLBACK) {
if let DecodeHintValue::NeedResultPointCallback(cb) = rpcb {
for point in points {
cb(point);
}
}
}
let mut result = RXingResult::new_complex(
decoderRXingResult.getText(),
decoderRXingResult.getRawBytes().clone(),
decoderRXingResult.getNumBits(),
points.to_vec(),
BarcodeFormat::AZTEC,
std::time::SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_millis(),
);
let byteSegments = decoderRXingResult.getByteSegments();
if !byteSegments.is_empty() {
result.putMetadata(RXingResultMetadataType::BYTE_SEGMENTS, RXingResultMetadataValue::ByteSegments(byteSegments.clone()));
}
let ecLevel = decoderRXingResult.getECLevel();
if !ecLevel.is_empty() {
result.putMetadata(RXingResultMetadataType::ERROR_CORRECTION_LEVEL, RXingResultMetadataValue::ErrorCorrectionLevel(ecLevel.to_owned()));
}
result.putMetadata(
RXingResultMetadataType::SYMBOLOGY_IDENTIFIER,
RXingResultMetadataValue::SymbologyIdentifier(format!("]z{}", decoderRXingResult.getSymbologyModifier())),
);
Ok(result)
}
fn reset() {
// do nothing
}
}

157
src/aztec/aztec_writer.rs Normal file
View File

@@ -0,0 +1,157 @@
/*
* Copyright 2013 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::collections::HashMap;
use encoding::EncodingRef;
use crate::{
common::BitMatrix, exceptions::Exceptions, BarcodeFormat, EncodeHintType, EncodeHintValue,
Writer,
};
use super::encoder::{encoder, AztecCode};
/**
* Renders an Aztec code as a {@link BitMatrix}.
*/
pub struct AztecWriter;
impl Writer for AztecWriter {
fn encode(
contents: &str,
format: &crate::BarcodeFormat,
width: i32,
height: i32,
) -> Result<crate::common::BitMatrix, crate::exceptions::Exceptions> {
Self::encode_with_hints(contents, format, width, height, &HashMap::new())
}
fn encode_with_hints(
contents: &str,
format: &crate::BarcodeFormat,
width: i32,
height: i32,
hints: &std::collections::HashMap<crate::EncodeHintType, crate::EncodeHintValue>,
) -> Result<crate::common::BitMatrix, crate::exceptions::Exceptions> {
let mut charset = None; // Do not add any ECI code by default
let mut eccPercent = encoder::DEFAULT_EC_PERCENT;
let mut layers = encoder::DEFAULT_AZTEC_LAYERS;
if hints.contains_key(&EncodeHintType::CHARACTER_SET) {
if let EncodeHintValue::CharacterSet(cset_name) = hints
.get(&EncodeHintType::CHARACTER_SET)
.expect("already knonw presence")
{
charset = Some(encoding::label::encoding_from_whatwg_label(cset_name).unwrap());
}
// charset = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString());
}
if hints.contains_key(&EncodeHintType::ERROR_CORRECTION) {
if let EncodeHintValue::ErrorCorrection(ecc_level) = hints
.get(&EncodeHintType::ERROR_CORRECTION)
.expect("key exists")
{
eccPercent = ecc_level.parse().expect("should convert to int");
}
// eccPercent = Integer.parseInt(hints.get(EncodeHintType::ERROR_CORRECTION).toString());
}
if hints.contains_key(&EncodeHintType::AZTEC_LAYERS) {
if let EncodeHintValue::AztecLayers(az_layers) = hints
.get(&EncodeHintType::AZTEC_LAYERS)
.expect("key exists")
{
layers = *az_layers;
}
// layers = Integer.parseInt(hints.get(EncodeHintType.AZTEC_LAYERS).toString());
}
encode(
contents,
*format,
width as u32,
height as u32,
charset,
eccPercent,
layers,
)
}
}
fn encode(
contents: &str,
format: BarcodeFormat,
width: u32,
height: u32,
charset: Option<EncodingRef>,
eccPercent: u32,
layers: u32,
) -> Result<BitMatrix, Exceptions> {
if format != BarcodeFormat::AZTEC {
return Err(Exceptions::IllegalArgumentException(format!(
"Can only encode AZTEC, but got {:?}",
format
)));
}
let aztec = if let Some(cset) = charset {
encoder::encode_with_charset(contents, eccPercent, layers, cset)?
} else {
encoder::encode(contents, eccPercent, layers)?
};
renderRXingResult(&aztec, width, height)
}
fn renderRXingResult(code: &AztecCode, width: u32, height: u32) -> Result<BitMatrix, Exceptions> {
let input = code.getMatrix();
// if input == null {
// throw new IllegalStateException();
// }
let inputWidth = input.getWidth();
let inputHeight = input.getHeight();
let outputWidth = width.max(inputWidth);
let outputHeight = height.max(inputHeight);
let multiple = (outputWidth / inputWidth).min(outputHeight / inputHeight);
let leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
let topPadding = (outputHeight - (inputHeight * multiple)) / 2;
let mut output = BitMatrix::new(outputWidth, outputHeight)?;
let mut inputY = 0;
let mut outputY = topPadding;
while inputY < inputHeight {
let mut inputX = 0;
let mut outputX = leftPadding;
while inputX < inputWidth {
if input.get(inputX, inputY) {
output.setRegion(outputX, outputY, multiple, multiple);
}
inputX += 1;
outputX += multiple;
}
inputY += 1;
outputY += multiple
}
// for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
// // Write the contents of this row of the barcode
// for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
// if (input.get(inputX, inputY)) {
// output.setRegion(outputX, outputY, multiple, multiple);
// }
// }
// }
Ok(output)
}

View File

@@ -735,7 +735,7 @@ impl Detector {
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
struct Point {
pub struct Point {
x: i32,
y: i32,
}

View File

@@ -1,404 +0,0 @@
/*
* Copyright 2013 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.aztec.encoder;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.reedsolomon.GenericGF;
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Generates Aztec 2D barcodes.
*
* @author Rustam Abdullaev
*/
public final class Encoder {
public static final int DEFAULT_EC_PERCENT = 33; // default minimal percentage of error check words
public static final int DEFAULT_AZTEC_LAYERS = 0;
private static final int MAX_NB_BITS = 32;
private static final int MAX_NB_BITS_COMPACT = 4;
private static final int[] WORD_SIZE = {
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12
};
private Encoder() {
}
/**
* Encodes the given string content as an Aztec symbol (without ECI code)
*
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(String data) {
return encode(data.getBytes(StandardCharsets.ISO_8859_1));
}
/**
* Encodes the given string content as an Aztec symbol (without ECI code)
*
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(String data, int minECCPercent, int userSpecifiedLayers) {
return encode(data.getBytes(StandardCharsets.ISO_8859_1), minECCPercent, userSpecifiedLayers, null);
}
/**
* Encodes the given string content as an Aztec symbol
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @param charset character set in which to encode string using ECI; if null, no ECI code
* will be inserted, and the string must be encodable as ISO/IEC 8859-1
* (Latin-1), the default encoding of the symbol.
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(String data, int minECCPercent, int userSpecifiedLayers, Charset charset) {
byte[] bytes = data.getBytes(null != charset ? charset : StandardCharsets.ISO_8859_1);
return encode(bytes, minECCPercent, userSpecifiedLayers, charset);
}
/**
* Encodes the given binary content as an Aztec symbol (without ECI code)
*
* @param data input data string
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(byte[] data) {
return encode(data, DEFAULT_EC_PERCENT, DEFAULT_AZTEC_LAYERS, null);
}
/**
* Encodes the given binary content as an Aztec symbol (without ECI code)
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers) {
return encode(data, minECCPercent, userSpecifiedLayers, null);
}
/**
* Encodes the given binary content as an Aztec symbol
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @param charset character set to mark using ECI; if null, no ECI code will be inserted, and the
* default encoding of ISO/IEC 8859-1 will be assuming by readers.
* @return Aztec symbol matrix with metadata
*/
public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers, Charset charset) {
// High-level encode
BitArray bits = new HighLevelEncoder(data, charset).encode();
// stuff bits and choose symbol size
int eccBits = bits.getSize() * minECCPercent / 100 + 11;
int totalSizeBits = bits.getSize() + eccBits;
boolean compact;
int layers;
int totalBitsInLayer;
int wordSize;
BitArray stuffedBits;
if (userSpecifiedLayers != DEFAULT_AZTEC_LAYERS) {
compact = userSpecifiedLayers < 0;
layers = Math.abs(userSpecifiedLayers);
if (layers > (compact ? MAX_NB_BITS_COMPACT : MAX_NB_BITS)) {
throw new IllegalArgumentException(
String.format("Illegal value %s for layers", userSpecifiedLayers));
}
totalBitsInLayer = totalBitsInLayer(layers, compact);
wordSize = WORD_SIZE[layers];
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
stuffedBits = stuffBits(bits, wordSize);
if (stuffedBits.getSize() + eccBits > usableBitsInLayers) {
throw new IllegalArgumentException("Data to large for user specified layer");
}
if (compact && stuffedBits.getSize() > wordSize * 64) {
// Compact format only allows 64 data words, though C4 can hold more words than that
throw new IllegalArgumentException("Data to large for user specified layer");
}
} else {
wordSize = 0;
stuffedBits = null;
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
// is the same size, but has more data.
for (int i = 0; ; i++) {
if (i > MAX_NB_BITS) {
throw new IllegalArgumentException("Data too large for an Aztec code");
}
compact = i <= 3;
layers = compact ? i + 1 : i;
totalBitsInLayer = totalBitsInLayer(layers, compact);
if (totalSizeBits > totalBitsInLayer) {
continue;
}
// [Re]stuff the bits if this is the first opportunity, or if the
// wordSize has changed
if (stuffedBits == null || wordSize != WORD_SIZE[layers]) {
wordSize = WORD_SIZE[layers];
stuffedBits = stuffBits(bits, wordSize);
}
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
if (compact && stuffedBits.getSize() > wordSize * 64) {
// Compact format only allows 64 data words, though C4 can hold more words than that
continue;
}
if (stuffedBits.getSize() + eccBits <= usableBitsInLayers) {
break;
}
}
}
BitArray messageBits = generateCheckWords(stuffedBits, totalBitsInLayer, wordSize);
// generate mode message
int messageSizeInWords = stuffedBits.getSize() / wordSize;
BitArray modeMessage = generateModeMessage(compact, layers, messageSizeInWords);
// allocate symbol
int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines
int[] alignmentMap = new int[baseMatrixSize];
int matrixSize;
if (compact) {
// no alignment marks in compact mode, alignmentMap is a no-op
matrixSize = baseMatrixSize;
for (int i = 0; i < alignmentMap.length; i++) {
alignmentMap[i] = i;
}
} else {
matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
int origCenter = baseMatrixSize / 2;
int center = matrixSize / 2;
for (int i = 0; i < origCenter; i++) {
int newOffset = i + i / 15;
alignmentMap[origCenter - i - 1] = center - newOffset - 1;
alignmentMap[origCenter + i] = center + newOffset + 1;
}
}
BitMatrix matrix = new BitMatrix(matrixSize);
// draw data bits
for (int i = 0, rowOffset = 0; i < layers; i++) {
int rowSize = (layers - i) * 4 + (compact ? 9 : 12);
for (int j = 0; j < rowSize; j++) {
int columnOffset = j * 2;
for (int k = 0; k < 2; k++) {
if (messageBits.get(rowOffset + columnOffset + k)) {
matrix.set(alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]);
}
if (messageBits.get(rowOffset + rowSize * 2 + columnOffset + k)) {
matrix.set(alignmentMap[i * 2 + j], alignmentMap[baseMatrixSize - 1 - i * 2 - k]);
}
if (messageBits.get(rowOffset + rowSize * 4 + columnOffset + k)) {
matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - k], alignmentMap[baseMatrixSize - 1 - i * 2 - j]);
}
if (messageBits.get(rowOffset + rowSize * 6 + columnOffset + k)) {
matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - j], alignmentMap[i * 2 + k]);
}
}
}
rowOffset += rowSize * 8;
}
// draw mode message
drawModeMessage(matrix, compact, matrixSize, modeMessage);
// draw alignment marks
if (compact) {
drawBullsEye(matrix, matrixSize / 2, 5);
} else {
drawBullsEye(matrix, matrixSize / 2, 7);
for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) {
for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
matrix.set(matrixSize / 2 - j, k);
matrix.set(matrixSize / 2 + j, k);
matrix.set(k, matrixSize / 2 - j);
matrix.set(k, matrixSize / 2 + j);
}
}
}
AztecCode aztec = new AztecCode();
aztec.setCompact(compact);
aztec.setSize(matrixSize);
aztec.setLayers(layers);
aztec.setCodeWords(messageSizeInWords);
aztec.setMatrix(matrix);
return aztec;
}
private static void drawBullsEye(BitMatrix matrix, int center, int size) {
for (int i = 0; i < size; i += 2) {
for (int j = center - i; j <= center + i; j++) {
matrix.set(j, center - i);
matrix.set(j, center + i);
matrix.set(center - i, j);
matrix.set(center + i, j);
}
}
matrix.set(center - size, center - size);
matrix.set(center - size + 1, center - size);
matrix.set(center - size, center - size + 1);
matrix.set(center + size, center - size);
matrix.set(center + size, center - size + 1);
matrix.set(center + size, center + size - 1);
}
static BitArray generateModeMessage(boolean compact, int layers, int messageSizeInWords) {
BitArray modeMessage = new BitArray();
if (compact) {
modeMessage.appendBits(layers - 1, 2);
modeMessage.appendBits(messageSizeInWords - 1, 6);
modeMessage = generateCheckWords(modeMessage, 28, 4);
} else {
modeMessage.appendBits(layers - 1, 5);
modeMessage.appendBits(messageSizeInWords - 1, 11);
modeMessage = generateCheckWords(modeMessage, 40, 4);
}
return modeMessage;
}
private static void drawModeMessage(BitMatrix matrix, boolean compact, int matrixSize, BitArray modeMessage) {
int center = matrixSize / 2;
if (compact) {
for (int i = 0; i < 7; i++) {
int offset = center - 3 + i;
if (modeMessage.get(i)) {
matrix.set(offset, center - 5);
}
if (modeMessage.get(i + 7)) {
matrix.set(center + 5, offset);
}
if (modeMessage.get(20 - i)) {
matrix.set(offset, center + 5);
}
if (modeMessage.get(27 - i)) {
matrix.set(center - 5, offset);
}
}
} else {
for (int i = 0; i < 10; i++) {
int offset = center - 5 + i + i / 5;
if (modeMessage.get(i)) {
matrix.set(offset, center - 7);
}
if (modeMessage.get(i + 10)) {
matrix.set(center + 7, offset);
}
if (modeMessage.get(29 - i)) {
matrix.set(offset, center + 7);
}
if (modeMessage.get(39 - i)) {
matrix.set(center - 7, offset);
}
}
}
}
private static BitArray generateCheckWords(BitArray bitArray, int totalBits, int wordSize) {
// bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
int messageSizeInWords = bitArray.getSize() / wordSize;
ReedSolomonEncoder rs = new ReedSolomonEncoder(getGF(wordSize));
int totalWords = totalBits / wordSize;
int[] messageWords = bitsToWords(bitArray, wordSize, totalWords);
rs.encode(messageWords, totalWords - messageSizeInWords);
int startPad = totalBits % wordSize;
BitArray messageBits = new BitArray();
messageBits.appendBits(0, startPad);
for (int messageWord : messageWords) {
messageBits.appendBits(messageWord, wordSize);
}
return messageBits;
}
private static int[] bitsToWords(BitArray stuffedBits, int wordSize, int totalWords) {
int[] message = new int[totalWords];
int i;
int n;
for (i = 0, n = stuffedBits.getSize() / wordSize; i < n; i++) {
int value = 0;
for (int j = 0; j < wordSize; j++) {
value |= stuffedBits.get(i * wordSize + j) ? (1 << wordSize - j - 1) : 0;
}
message[i] = value;
}
return message;
}
private static GenericGF getGF(int wordSize) {
switch (wordSize) {
case 4:
return GenericGF.AZTEC_PARAM;
case 6:
return GenericGF.AZTEC_DATA_6;
case 8:
return GenericGF.AZTEC_DATA_8;
case 10:
return GenericGF.AZTEC_DATA_10;
case 12:
return GenericGF.AZTEC_DATA_12;
default:
throw new IllegalArgumentException("Unsupported word size " + wordSize);
}
}
static BitArray stuffBits(BitArray bits, int wordSize) {
BitArray out = new BitArray();
int n = bits.getSize();
int mask = (1 << wordSize) - 2;
for (int i = 0; i < n; i += wordSize) {
int word = 0;
for (int j = 0; j < wordSize; j++) {
if (i + j >= n || bits.get(i + j)) {
word |= 1 << (wordSize - 1 - j);
}
}
if ((word & mask) == mask) {
out.appendBits(word & mask, wordSize);
i--;
} else if ((word & mask) == 0) {
out.appendBits(word | 1, wordSize);
i--;
} else {
out.appendBits(word, wordSize);
}
}
return out;
}
private static int totalBitsInLayer(int layers, boolean compact) {
return ((compact ? 88 : 112) + 16 * layers) * layers;
}
}

View File

@@ -30,6 +30,16 @@ pub struct AztecCode {
}
impl AztecCode {
pub fn new(compact: bool, size: u32, layers: u32, code_words: u32, matrix: BitMatrix) -> Self {
Self {
compact,
size,
layers,
code_words,
matrix,
}
}
/**
* @return {@code true} if compact instead of full mode
*/

View File

@@ -18,7 +18,7 @@ use std::fmt;
use crate::common::BitArray;
#[derive(Debug,PartialEq, Eq,Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct BinaryShiftToken {
binaryShiftStart: u32,
binaryShiftByteCount: u32,
@@ -44,7 +44,7 @@ impl BinaryShiftToken {
bitArray.appendBits(bsbc as u32 - 31, 16);
} else if (i == 0) {
// 1 <= binaryShiftByteCode <= 62
bitArray.appendBits(bsbc.min( 31) as u32, 5);
bitArray.appendBits(bsbc.min(31) as u32, 5);
} else {
// 32 <= binaryShiftCount <= 62 and i == 31
bitArray.appendBits(bsbc as u32 - 31, 5);

View File

@@ -0,0 +1,523 @@
/*
* Copyright 2013 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 encoding::Encoding;
use crate::{
common::{
reedsolomon::{
get_predefined_genericgf, GenericGF, PredefinedGenericGF, ReedSolomonEncoder,
},
BitArray, BitMatrix,
},
exceptions::Exceptions,
};
use super::{AztecCode, HighLevelEncoder};
/**
* Generates Aztec 2D barcodes.
*
* @author Rustam Abdullaev
*/
pub const DEFAULT_EC_PERCENT: u32 = 33; // default minimal percentage of error check words
pub const DEFAULT_AZTEC_LAYERS: u32 = 0;
pub const MAX_NB_BITS: u32 = 32;
pub const MAX_NB_BITS_COMPACT: u32 = 4;
pub const WORD_SIZE: [u32; 33] = [
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12,
];
/**
* Encodes the given string content as an Aztec symbol (without ECI code)
*
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
* @return Aztec symbol matrix with metadata
*/
pub fn encode_simple(data: &str) -> Result<AztecCode, Exceptions> {
let bytes = encoding::all::ISO_8859_1
.encode(data, encoding::EncoderTrap::Replace)
.unwrap();
encode_bytes_simple(&bytes)
}
/**
* Encodes the given string content as an Aztec symbol (without ECI code)
*
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @return Aztec symbol matrix with metadata
*/
pub fn encode(
data: &str,
minECCPercent: u32,
userSpecifiedLayers: u32,
) -> Result<AztecCode, Exceptions> {
let bytes = encoding::all::ISO_8859_1
.encode(data, encoding::EncoderTrap::Replace)
.unwrap();
encode_bytes(&bytes, minECCPercent, userSpecifiedLayers)
}
/**
* Encodes the given string content as an Aztec symbol
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @param charset character set in which to encode string using ECI; if null, no ECI code
* will be inserted, and the string must be encodable as ISO/IEC 8859-1
* (Latin-1), the default encoding of the symbol.
* @return Aztec symbol matrix with metadata
*/
pub fn encode_with_charset(
data: &str,
minECCPercent: u32,
userSpecifiedLayers: u32,
charset: &'static dyn encoding::Encoding,
) -> Result<AztecCode, Exceptions> {
let bytes = charset
.encode(data, encoding::EncoderTrap::Replace)
.unwrap(); //data.getBytes(null != charset ? charset : StandardCharsets.ISO_8859_1);
encode_bytes_with_charset(&bytes, minECCPercent, userSpecifiedLayers, charset)
}
/**
* Encodes the given binary content as an Aztec symbol (without ECI code)
*
* @param data input data string
* @return Aztec symbol matrix with metadata
*/
pub fn encode_bytes_simple(data: &[u8]) -> Result<AztecCode, Exceptions> {
encode_bytes(data, DEFAULT_EC_PERCENT, DEFAULT_AZTEC_LAYERS)
}
/**
* Encodes the given binary content as an Aztec symbol (without ECI code)
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @return Aztec symbol matrix with metadata
*/
pub fn encode_bytes(
data: &[u8],
minECCPercent: u32,
userSpecifiedLayers: u32,
) -> Result<AztecCode, Exceptions> {
encode_bytes_with_charset(
data,
minECCPercent,
userSpecifiedLayers,
encoding::all::ISO_8859_1,
)
}
/**
* Encodes the given binary content as an Aztec symbol
*
* @param data input data string
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
* a minimum of 23% + 3 words is recommended)
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
* @param charset character set to mark using ECI; if null, no ECI code will be inserted, and the
* default encoding of ISO/IEC 8859-1 will be assuming by readers.
* @return Aztec symbol matrix with metadata
*/
pub fn encode_bytes_with_charset(
data: &[u8],
minECCPercent: u32,
userSpecifiedLayers: u32,
charset: &'static dyn encoding::Encoding,
) -> Result<AztecCode, Exceptions> {
// High-level encode
let bits = HighLevelEncoder::with_charset(data.into(), charset).encode()?;
// stuff bits and choose symbol size
let eccBits = bits.getSize() as u32 * minECCPercent / 100 + 11;
let totalSizeBits = bits.getSize() as u32 + eccBits;
let mut compact;
let mut layers;
let mut totalBitsInLayerVar;
let mut wordSize;
let mut stuffedBits;
if userSpecifiedLayers != DEFAULT_AZTEC_LAYERS {
compact = userSpecifiedLayers < 0;
layers = userSpecifiedLayers;
if layers
> (if compact {
MAX_NB_BITS_COMPACT
} else {
MAX_NB_BITS
})
{
return Err(Exceptions::IllegalArgumentException(format!(
"Illegal value {} for layers",
userSpecifiedLayers
)));
}
totalBitsInLayerVar = totalBitsInLayer(layers, compact);
wordSize = WORD_SIZE[layers as usize];
let usableBitsInLayers = totalBitsInLayerVar - (totalBitsInLayerVar % wordSize);
stuffedBits = stuffBits(&bits, wordSize as usize);
if stuffedBits.getSize() as u32+ eccBits > usableBitsInLayers {
return Err(Exceptions::IllegalArgumentException(
"Data to large for user specified layer".to_owned(),
));
}
if compact && stuffedBits.getSize() as u32 > wordSize * 64 {
// Compact format only allows 64 data words, though C4 can hold more words than that
return Err(Exceptions::IllegalArgumentException(
"Data to large for user specified layer".to_owned(),
));
}
} else {
wordSize = 0;
stuffedBits = BitArray::new();
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
// is the same size, but has more data.
let mut i = 0;
loop {
// for (int i = 0; ; i++) {
if i > MAX_NB_BITS {
return Err(Exceptions::IllegalArgumentException(
"Data too large for an Aztec code".to_owned(),
));
}
compact = i <= 3;
layers = if compact { i + 1 } else { i };
totalBitsInLayerVar = totalBitsInLayer(layers, compact);
if totalSizeBits > totalBitsInLayerVar as u32 {
continue;
}
// [Re]stuff the bits if this is the first opportunity, or if the
// wordSize has changed
if stuffedBits.getSize() == 0 || wordSize != WORD_SIZE[layers as usize] {
wordSize = WORD_SIZE[layers as usize];
stuffedBits = stuffBits(&bits, wordSize as usize);
}
let usableBitsInLayers = totalBitsInLayerVar - (totalBitsInLayerVar % wordSize);
if compact && stuffedBits.getSize() as u32 > wordSize * 64 {
// Compact format only allows 64 data words, though C4 can hold more words than that
continue;
}
if stuffedBits.getSize()as u32 + eccBits<= usableBitsInLayers {
break;
}
i += 1;
}
}
let messageBits = generateCheckWords(
&stuffedBits,
totalBitsInLayerVar as usize,
wordSize as usize,
);
// generate mode message
let messageSizeInWords = stuffedBits.getSize() as u32 / wordSize;
let modeMessage = generateModeMessage(compact, layers as u32, messageSizeInWords);
// allocate symbol
let baseMatrixSize = (if compact { 11 } else { 14 }) + layers * 4; // not including alignment lines
let mut alignmentMap = vec![0u32; baseMatrixSize as usize];
let matrixSize;
if compact {
// no alignment marks in compact mode, alignmentMap is a no-op
matrixSize = baseMatrixSize;
for i in 0..alignmentMap.len() {
// for (int i = 0; i < alignmentMap.length; i++) {
alignmentMap[i] = i as u32;
}
} else {
matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
let origCenter = (baseMatrixSize / 2) as usize;
let center = matrixSize / 2;
for i in 0..origCenter {
// for (int i = 0; i < origCenter; i++) {
let newOffset = (i + i / 15) as u32;
alignmentMap[origCenter - i - 1] = center as u32 - newOffset - 1;
alignmentMap[origCenter + i] = center as u32 + newOffset + 1;
}
}
let mut matrix = BitMatrix::with_single_dimension(matrixSize as u32);
// draw data bits
let mut rowOffset = 0;
for i in 0..layers as usize {
// for (int i = 0, rowOffset = 0; i < layers; i++) {
let rowSize = (layers as usize - i) * 4 + (if compact { 9 } else { 12 });
for j in 0..rowSize {
// for (int j = 0; j < rowSize; j++) {
let columnOffset = j * 2;
for k in 0..2 {
// for (int k = 0; k < 2; k++) {
if messageBits.get(rowOffset + columnOffset + k) {
matrix.set(alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]);
}
if messageBits.get(rowOffset + rowSize * 2 + columnOffset + k) {
matrix.set(
alignmentMap[i * 2 + j],
alignmentMap[baseMatrixSize as usize - 1 - i * 2 - k],
);
}
if messageBits.get(rowOffset + rowSize * 4 + columnOffset + k) {
matrix.set(
alignmentMap[baseMatrixSize as usize - 1 - i * 2 - k],
alignmentMap[baseMatrixSize as usize - 1 - i * 2 - j],
);
}
if messageBits.get(rowOffset + rowSize * 6 + columnOffset + k) {
matrix.set(
alignmentMap[baseMatrixSize as usize - 1 - i * 2 - j],
alignmentMap[i * 2 + k],
);
}
}
}
rowOffset += rowSize * 8;
}
// draw mode message
drawModeMessage(&mut matrix, compact, matrixSize as u32, modeMessage);
// draw alignment marks
if compact {
drawBullsEye(&mut matrix, matrixSize as u32 / 2, 5);
} else {
drawBullsEye(&mut matrix, matrixSize as u32 / 2, 7);
let mut i = 0;
let mut j = 0;
while i < baseMatrixSize / 2 - 1 {
let mut k = (matrixSize / 2) & 1;
while k < matrixSize {
// for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
matrix.set(matrixSize as u32 / 2 - j, k as u32);
matrix.set(matrixSize as u32 / 2 + j, k as u32);
matrix.set(k as u32, matrixSize as u32 / 2 - j);
matrix.set(k as u32, matrixSize as u32 / 2 + j);
k += 2;
}
i += 15;
j += 16;
}
// for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) {
// for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
// matrix.set(matrixSize / 2 - j, k);
// matrix.set(matrixSize / 2 + j, k);
// matrix.set(k, matrixSize / 2 - j);
// matrix.set(k, matrixSize / 2 + j);
// }
// }
}
let aztec = AztecCode::new(
compact,
matrixSize as u32,
layers,
messageSizeInWords as u32,
matrix,
);
// aztec.setCompact(compact);
// aztec.setSize(matrixSize);
// aztec.setLayers(layers);
// aztec.setCodeWords(messageSizeInWords);
// aztec.setMatrix(matrix);
Ok(aztec)
}
fn drawBullsEye(matrix: &mut BitMatrix, center: u32, size: u32) {
let mut i = 0;
while i < size {
// for (int i = 0; i < size; i += 2) {
for j in (center - 1)..=(center + 1) {
// for (int j = center - i; j <= center + i; j++) {
matrix.set(j, center - i);
matrix.set(j, center + i);
matrix.set(center - i, j);
matrix.set(center + i, j);
}
i += 2;
}
matrix.set(center - size, center - size);
matrix.set(center - size + 1, center - size);
matrix.set(center - size, center - size + 1);
matrix.set(center + size, center - size);
matrix.set(center + size, center - size + 1);
matrix.set(center + size, center + size - 1);
}
pub fn generateModeMessage(compact: bool, layers: u32, messageSizeInWords: u32) -> BitArray {
let mut modeMessage = BitArray::new();
if compact {
modeMessage.appendBits(layers - 1, 2);
modeMessage.appendBits(messageSizeInWords - 1, 6);
modeMessage = generateCheckWords(&modeMessage, 28, 4);
} else {
modeMessage.appendBits(layers - 1, 5);
modeMessage.appendBits(messageSizeInWords - 1, 11);
modeMessage = generateCheckWords(&modeMessage, 40, 4);
}
return modeMessage;
}
fn drawModeMessage(matrix: &mut BitMatrix, compact: bool, matrixSize: u32, modeMessage: BitArray) {
let center = matrixSize / 2;
if compact {
for i in 0..7usize {
// for (int i = 0; i < 7; i++) {
let offset = (center as usize - 3 + i) as u32;
if modeMessage.get(i) {
matrix.set(offset, center - 5);
}
if modeMessage.get(i + 7) {
matrix.set(center + 5, offset);
}
if modeMessage.get(20 - i) {
matrix.set(offset, center + 5);
}
if modeMessage.get(27 - i) {
matrix.set(center - 5, offset);
}
}
} else {
for i in 0..10usize {
// for (int i = 0; i < 10; i++) {
let offset = (center as usize - 5 + i + i / 5) as u32;
if modeMessage.get(i) {
matrix.set(offset, center - 7);
}
if modeMessage.get(i + 10) {
matrix.set(center + 7, offset);
}
if modeMessage.get(29 - i) {
matrix.set(offset, center + 7);
}
if modeMessage.get(39 - i) {
matrix.set(center - 7, offset);
}
}
}
}
fn generateCheckWords(bitArray: &BitArray, totalBits: usize, wordSize: usize) -> BitArray {
// bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
let messageSizeInWords = bitArray.getSize() / wordSize;
let mut rs = ReedSolomonEncoder::new(getGF(wordSize).expect("Should never have bad value"));
let totalWords = totalBits / wordSize;
let mut messageWords = bitsToWords(bitArray, wordSize, totalWords);
rs.encode(&mut messageWords, totalWords - messageSizeInWords);
let startPad = totalBits % wordSize;
let mut messageBits = BitArray::new();
messageBits.appendBits(0, startPad);
for messageWord in messageWords {
// for (int messageWord : messageWords) {
messageBits.appendBits(messageWord as u32, wordSize);
}
return messageBits;
}
fn bitsToWords(stuffedBits: &BitArray, wordSize: usize, totalWords: usize) -> Vec<i32> {
let mut message = vec![0i32; totalWords];
// let i=0;
// let n;
for i in 0..(stuffedBits.getSize() / wordSize) {
// for (i = 0, n = stuffedBits.getSize() / wordSize; i < n; i++) {
let mut value = 0;
for j in 0..wordSize {
// for (int j = 0; j < wordSize; j++) {
value |= if stuffedBits.get(i * wordSize + j) {
1 << wordSize - j - 1
} else {
0
};
}
message[i] = value;
}
return message;
}
fn getGF(wordSize: usize) -> Result<GenericGF, Exceptions> {
match wordSize {
4 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecParam)),
6 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecData6)),
8 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecData8)),
10 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecData10)),
12 => Ok(get_predefined_genericgf(PredefinedGenericGF::AztecData12)),
_ => Err(Exceptions::IllegalArgumentException(format!(
"Unsupported word size {}",
wordSize
))),
}
// switch (wordSize) {
// case 4:
// return GenericGF.AZTEC_PARAM;
// case 6:
// return GenericGF.AZTEC_DATA_6;
// case 8:
// return GenericGF.AZTEC_DATA_8;
// case 10:
// return GenericGF.AZTEC_DATA_10;
// case 12:
// return GenericGF.AZTEC_DATA_12;
// default:
// throw new IllegalArgumentException("Unsupported word size " + wordSize);
// }
}
pub fn stuffBits(bits: &BitArray, wordSize: usize) -> BitArray {
let mut out = BitArray::new();
let n = bits.getSize();
let mask = (1 << wordSize) - 2;
let mut i = 0;
while i < n {
// for (int i = 0; i < n; i += wordSize) {
let mut word = 0;
for j in 0..wordSize {
// for (int j = 0; j < wordSize; j++) {
if i + j >= n || bits.get(i + j) {
word |= 1 << (wordSize - 1 - j);
}
}
if (word & mask) == mask {
out.appendBits(word & mask, wordSize);
i -= 1;
} else if (word & mask) == 0 {
out.appendBits(word | 1, wordSize);
i -= 1;
} else {
out.appendBits(word, wordSize);
}
i += wordSize;
}
return out;
}
fn totalBitsInLayer(layers: u32, compact: bool) -> u32 {
((if compact { 88 } else { 112 }) + 16 * layers) * layers
// return ((compact ? 88 : 112) + 16 * layers) * layers;
}

View File

@@ -14,9 +14,14 @@
* limitations under the License.
*/
use crate::common::BitArray;
use std::cmp::Ordering;
use crate::{
common::{BitArray, CharacterSetECI},
exceptions::Exceptions,
};
use super::{State, Token};
/**
* This produces nearly optimal encodings of text into the first-level of
@@ -31,342 +36,425 @@ use crate::common::BitArray;
* @author Rustam Abdullaev
*/
pub struct HighLevelEncoder {
text:Vec<u8>,
charset: &'static dyn encoding::Encoding,
text: Vec<u8>,
charset: &'static dyn encoding::Encoding,
}
impl HighLevelEncoder {
pub const MODE_NAMES : [&str] = ["UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"];
pub const MODE_NAMES: [&'static str; 5] = ["UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"];
const MODE_UPPER :u32= 0; // 5 bits
const MODE_LOWER :u32 = 1; // 5 bits
const MODE_DIGIT :u32 = 2; // 4 bits
const MODE_MIXED :u32 = 3; // 5 bits
const MODE_PUNCT :u32 = 4; // 5 bits
pub const MODE_UPPER: usize = 0; // 5 bits
pub const MODE_LOWER: usize = 1; // 5 bits
pub const MODE_DIGIT: usize = 2; // 4 bits
pub const MODE_MIXED: usize = 3; // 5 bits
pub const MODE_PUNCT: usize = 4; // 5 bits
// The Latch Table shows, for each pair of Modes, the optimal method for
// getting from one mode to another. In the worst possible case, this can
// be up to 14 bits. In the best possible case, we are already there!
// The high half-word of each entry gives the number of bits.
// The low half-word of each entry are the actual bits necessary to change
const LATCH_TABLE : [[u32]]= [
[
0,
(5 << 16) + 28, // UPPER -> LOWER
(5 << 16) + 30, // UPPER -> DIGIT
(5 << 16) + 29, // UPPER -> MIXED
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
],
[
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
0,
(5 << 16) + 30, // LOWER -> DIGIT
(5 << 16) + 29, // LOWER -> MIXED
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
],
[
(4 << 16) + 14, // DIGIT -> UPPER
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
0,
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
(14 << 16) + (14 << 10) + (29 << 5) + 30,
] // DIGIT -> UPPER -> MIXED -> PUNCT
,
[
(5 << 16) + 29, // MIXED -> UPPER
(5 << 16) + 28, // MIXED -> LOWER
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
0,
(5 << 16) + 30, // MIXED -> PUNCT
],
[
(5 << 16) + 31, // PUNCT -> UPPER
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
0,
],
];
// A reverse mapping from [mode][char] to the encoding for that character
// in that mode. An entry of 0 indicates no mapping exists.
const CHAR_MAP : [[u32]] = {
let char_map = vec![vec![0u32;256];5];
char_map[Self::MODE_UPPER][' '] = 1;
for (int c = 'A'; c <= 'Z'; c++) {
char_map[Self::MODE_UPPER][c] = c - 'A' + 2;
}
char_map[Self::MODE_LOWER][' '] = 1;
for (int c = 'a'; c <= 'z'; c++) {
char_map[Self::MODE_LOWER][c] = c - 'a' + 2;
}
char_map[Self::MODE_DIGIT][' '] = 1;
for (int c = '0'; c <= '9'; c++) {
char_map[Self::MODE_DIGIT][c] = c - '0' + 2;
}
char_map[Self::MODE_DIGIT][','] = 12;
char_map[Self::MODE_DIGIT]['.'] = 13;
let mixedTable = [
'\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
'\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
'_', '`', '|', '~', '\177'
// The Latch Table shows, for each pair of Modes, the optimal method for
// getting from one mode to another. In the worst possible case, this can
// be up to 14 bits. In the best possible case, we are already there!
// The high half-word of each entry gives the number of bits.
// The low half-word of each entry are the actual bits necessary to change
pub const LATCH_TABLE: [[u32; 5]; 5] = [
[
0,
(5 << 16) + 28, // UPPER -> LOWER
(5 << 16) + 30, // UPPER -> DIGIT
(5 << 16) + 29, // UPPER -> MIXED
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
],
[
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
0,
(5 << 16) + 30, // LOWER -> DIGIT
(5 << 16) + 29, // LOWER -> MIXED
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
],
[
(4 << 16) + 14, // DIGIT -> UPPER
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
0,
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
(14 << 16) + (14 << 10) + (29 << 5) + 30,
], // DIGIT -> UPPER -> MIXED -> PUNCT
[
(5 << 16) + 29, // MIXED -> UPPER
(5 << 16) + 28, // MIXED -> LOWER
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
0,
(5 << 16) + 30, // MIXED -> PUNCT
],
[
(5 << 16) + 31, // PUNCT -> UPPER
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
0,
],
];
for (int i = 0; i < mixedTable.length; i++) {
CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
}
let punctTable = [
'\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
'[', ']', '{', '}'
];
for (int i = 0; i < punctTable.length; i++) {
if (punctTable[i] > 0) {
CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
}
}
};
// private static final int[][] CHAR_MAP = new int[5][256];
// static {
// CHAR_MAP[MODE_UPPER][' '] = 1;
// for (int c = 'A'; c <= 'Z'; c++) {
// CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;
// }
// CHAR_MAP[MODE_LOWER][' '] = 1;
// for (int c = 'a'; c <= 'z'; c++) {
// CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;
// }
// CHAR_MAP[MODE_DIGIT][' '] = 1;
// for (int c = '0'; c <= '9'; c++) {
// CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;
// }
// CHAR_MAP[MODE_DIGIT][','] = 12;
// CHAR_MAP[MODE_DIGIT]['.'] = 13;
// int[] mixedTable = {
// '\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
// '\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
// '_', '`', '|', '~', '\177'
// };
// for (int i = 0; i < mixedTable.length; i++) {
// CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
// }
// int[] punctTable = {
// '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
// '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
// '[', ']', '{', '}'
// };
// for (int i = 0; i < punctTable.length; i++) {
// if (punctTable[i] > 0) {
// CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
// }
// }
// }
// A map showing the available shift codes. (The shifts to BINARY are not
// shown
const SHIFT_TABLE : [[i32]]= { // mode shift codes, per table
let mut shift_table = [[-1i32;6];6];
shift_table[Self::MODE_UPPER][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_LOWER][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_LOWER][Self::MODE_UPPER] = 28;
shift_table[Self::MODE_MIXED][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_DIGIT][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_DIGIT][Self::MODE_UPPER] = 15;
shift_table
};
// const SHIFT_TABLE : [[u32]]= new int[6][6]; // mode shift codes, per table
// static {
// for (int[] table : SHIFT_TABLE) {
// Arrays.fill(table, -1);
// }
// SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;
// SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;
// }
pub fn new(text:Vec<u8>) -> Self{
Self{
text,
charset: encoding::all::UTF_8,
}
}
pub fn with_charset(text:Vec<u8>, charset:&'static dyn encoding::Encoding) -> Self{
Self{
text,
charset,
}
}
/**
* @return text represented by this encoder encoded as a {@link BitArray}
*/
pub fn encode(&self)-> BitArray {
State initialState = State.INITIAL_STATE;
if (charset != null) {
CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(charset);
if (null == eci) {
throw new IllegalArgumentException("No ECI code for character set " + charset);
}
initialState = initialState.appendFLGn(eci.getValue());
}
Collection<State> states = Collections.singletonList(initialState);
for (int index = 0; index < text.length; index++) {
int pairCode;
int nextChar = index + 1 < text.length ? text[index + 1] : 0;
switch (text[index]) {
case '\r':
pairCode = nextChar == '\n' ? 2 : 0;
break;
case '.' :
pairCode = nextChar == ' ' ? 3 : 0;
break;
case ',' :
pairCode = nextChar == ' ' ? 4 : 0;
break;
case ':' :
pairCode = nextChar == ' ' ? 5 : 0;
break;
default:
pairCode = 0;
}
if (pairCode > 0) {
// We have one of the four special PUNCT pairs. Treat them specially.
// Get a new set of states for the two new characters.
states = updateStateListForPair(states, index, pairCode);
index++;
} else {
// Get a new set of states for the new character.
states = updateStateListForChar(states, index);
}
}
// We are left with a set of states. Find the shortest one.
State minState = Collections.min(states, new Comparator<State>() {
@Override
public int compare(State a, State b) {
return a.getBitCount() - b.getBitCount();
}
});
// Convert it to a bit array, and return.
return minState.toBitArray(text);
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
private Collection<State> updateStateListForChar(Iterable<State> states, int index) {
Collection<State> result = new LinkedList<>();
for (State state : states) {
updateStateForChar(state, index, result);
}
return simplifyStates(result);
}
// Return a set of states that represent the possible ways of updating this
// state for the next character. The resulting set of states are added to
// the "result" list.
private void updateStateForChar(State state, int index, Collection<State> result) {
char ch = (char) (text[index] & 0xFF);
boolean charInCurrentTable = CHAR_MAP[state.getMode()][ch] > 0;
State stateNoBinary = null;
for (int mode = 0; mode <= MODE_PUNCT; mode++) {
int charInMode = CHAR_MAP[mode][ch];
if (charInMode > 0) {
if (stateNoBinary == null) {
// Only create stateNoBinary the first time it's required.
stateNoBinary = state.endBinaryShift(index);
// A reverse mapping from [mode][char] to the encoding for that character
// in that mode. An entry of 0 indicates no mapping exists.
pub const CHAR_MAP: [[u8; 256]; 5] = {
let mut char_map = [[0u8; 256]; 5];
char_map[Self::MODE_UPPER][b' ' as usize] = 1;
let mut c = b'A';
while c <= b'Z' {
char_map[Self::MODE_UPPER][c as usize] = c - b'A' + 2;
c += 1;
}
// Try generating the character by latching to its mode
if (!charInCurrentTable || mode == state.getMode() || mode == MODE_DIGIT) {
// If the character is in the current table, we don't want to latch to
// any other mode except possibly digit (which uses only 4 bits). Any
// other latch would be equally successful *after* this character, and
// so wouldn't save any bits.
State latchState = stateNoBinary.latchAndAppend(mode, charInMode);
result.add(latchState);
// for (int c = 'A'; c <= 'Z'; c++) {
// char_map[Self::MODE_UPPER][c] = c - 'A' + 2;
// }
char_map[Self::MODE_LOWER][b' ' as usize] = 1;
let mut c = b'a';
while c <= b'z' {
char_map[Self::MODE_LOWER][c as usize] = c - b'a' + 2;
c += 1;
}
// Try generating the character by switching to its mode.
if (!charInCurrentTable && SHIFT_TABLE[state.getMode()][mode] >= 0) {
// It never makes sense to temporarily shift to another mode if the
// character exists in the current mode. That can never save bits.
State shiftState = stateNoBinary.shiftAndAppend(mode, charInMode);
result.add(shiftState);
// for (int c = 'a'; c <= 'z'; c++) {
// char_map[Self::MODE_LOWER][c] = c - 'a' + 2;
// }
char_map[Self::MODE_DIGIT][b' ' as usize] = 1;
let mut c = b'0';
while c <= b'9' {
char_map[Self::MODE_DIGIT][c as usize] = c - b'0' + 2;
c += 1;
}
}
}
if (state.getBinaryShiftByteCount() > 0 || CHAR_MAP[state.getMode()][ch] == 0) {
// It's never worthwhile to go into binary shift mode if you're not already
// in binary shift mode, and the character exists in your current mode.
// That can never save bits over just outputting the char in the current mode.
State binaryState = state.addBinaryShiftChar(index);
result.add(binaryState);
}
}
private static Collection<State> updateStateListForPair(Iterable<State> states, int index, int pairCode) {
Collection<State> result = new LinkedList<>();
for (State state : states) {
updateStateForPair(state, index, pairCode, result);
}
return simplifyStates(result);
}
private static void updateStateForPair(State state, int index, int pairCode, Collection<State> result) {
State stateNoBinary = state.endBinaryShift(index);
// Possibility 1. Latch to MODE_PUNCT, and then append this code
result.add(stateNoBinary.latchAndAppend(MODE_PUNCT, pairCode));
if (state.getMode() != MODE_PUNCT) {
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
// Every state except MODE_PUNCT (handled above) can shift
result.add(stateNoBinary.shiftAndAppend(MODE_PUNCT, pairCode));
}
if (pairCode == 3 || pairCode == 4) {
// both characters are in DIGITS. Sometimes better to just add two digits
State digitState = stateNoBinary
.latchAndAppend(MODE_DIGIT, 16 - pairCode) // period or comma in DIGIT
.latchAndAppend(MODE_DIGIT, 1); // space in DIGIT
result.add(digitState);
}
if (state.getBinaryShiftByteCount() > 0) {
// It only makes sense to do the characters as binary if we're already
// in binary mode.
State binaryState = state.addBinaryShiftChar(index).addBinaryShiftChar(index + 1);
result.add(binaryState);
}
}
private static Collection<State> simplifyStates(Iterable<State> states) {
Deque<State> result = new LinkedList<>();
for (State newState : states) {
boolean add = true;
for (Iterator<State> iterator = result.iterator(); iterator.hasNext();) {
State oldState = iterator.next();
if (oldState.isBetterThanOrEqualTo(newState)) {
add = false;
break;
// for (int c = '0'; c <= '9'; c++) {
// char_map[Self::MODE_DIGIT][c] = c - '0' + 2;
// }
char_map[Self::MODE_DIGIT][b',' as usize] = 12;
char_map[Self::MODE_DIGIT][b'.' as usize] = 13;
let mixedTable = [
'\0', ' ', '\u{1}', '\u{2}', '\u{3}', '\u{4}', '\u{5}', '\u{6}', '\u{7}', '\u{8}',
'\t', '\n', '\u{13}', '\u{f}', '\r', '\u{33}', '\u{34}', '\u{35}', '\u{36}', '\u{37}',
'@', '\\', '^', '_', '`', '|', '~', '\u{177}',
];
let mut i = 0;
while i < mixedTable.len() {
char_map[Self::MODE_MIXED][mixedTable[i] as u8 as usize] = i as u8;
i += 1;
}
if (newState.isBetterThanOrEqualTo(oldState)) {
iterator.remove();
// for (int i = 0; i < mixedTable.length; i++) {
// CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
// }
let punctTable = [
'\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'', '(', ')', '*',
'+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', ']', '{', '}',
];
let mut i = 0;
while i < punctTable.len() {
if punctTable[i] as u8 > 0u8 {
char_map[Self::MODE_PUNCT][punctTable[i] as u8 as usize] = i as u8;
}
i += 1;
}
}
if (add) {
result.addFirst(newState);
}
}
return result;
}
// for (int i = 0; i < punctTable.length; i++) {
// if (punctTable[i] > 0) {
// CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
// }
// }
char_map
};
// private static final int[][] CHAR_MAP = new int[5][256];
// static {
// CHAR_MAP[MODE_UPPER][' '] = 1;
// for (int c = 'A'; c <= 'Z'; c++) {
// CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;
// }
// CHAR_MAP[MODE_LOWER][' '] = 1;
// for (int c = 'a'; c <= 'z'; c++) {
// CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;
// }
// CHAR_MAP[MODE_DIGIT][' '] = 1;
// for (int c = '0'; c <= '9'; c++) {
// CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;
// }
// CHAR_MAP[MODE_DIGIT][','] = 12;
// CHAR_MAP[MODE_DIGIT]['.'] = 13;
// int[] mixedTable = {
// '\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
// '\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
// '_', '`', '|', '~', '\177'
// };
// for (int i = 0; i < mixedTable.length; i++) {
// CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
// }
// int[] punctTable = {
// '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
// '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
// '[', ']', '{', '}'
// };
// for (int i = 0; i < punctTable.length; i++) {
// if (punctTable[i] > 0) {
// CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
// }
// }
// }
// A map showing the available shift codes. (The shifts to BINARY are not
// shown
pub const SHIFT_TABLE: [[i32; 6]; 6] = {
// mode shift codes, per table
let mut shift_table = [[-1i32; 6]; 6];
shift_table[Self::MODE_UPPER][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_LOWER][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_LOWER][Self::MODE_UPPER] = 28;
shift_table[Self::MODE_MIXED][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_DIGIT][Self::MODE_PUNCT] = 0;
shift_table[Self::MODE_DIGIT][Self::MODE_UPPER] = 15;
shift_table
};
// const SHIFT_TABLE : [[u32]]= new int[6][6]; // mode shift codes, per table
// static {
// for (int[] table : SHIFT_TABLE) {
// Arrays.fill(table, -1);
// }
// SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;
// SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;
// SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;
// }
pub fn new(text: Vec<u8>) -> Self {
Self {
text,
charset: encoding::all::UTF_8,
}
}
pub fn with_charset(text: Vec<u8>, charset: &'static dyn encoding::Encoding) -> Self {
Self { text, charset }
}
/**
* @return text represented by this encoder encoded as a {@link BitArray}
*/
pub fn encode(&self) -> Result<BitArray, Exceptions> {
let mut initialState = State::new(Token::new(), Self::MODE_UPPER as u32, 0, 0);
if let Some(eci) = CharacterSetECI::getCharacterSetECI(self.charset) {
initialState = initialState.appendFLGn(CharacterSetECI::getValue(&eci))?;
} else {
return Err(Exceptions::IllegalArgumentException(
"No ECI code for character set".to_owned(),
));
}
// if self.charset != null {
// CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(charset);
// if (null == eci) {
// throw new IllegalArgumentException("No ECI code for character set " + charset);
// }
// initialState = initialState.appendFLGn(eci.getValue());
// }
let mut states = vec![initialState];
let mut index = 0;
while index < self.text.len() {
// for index in 0..self.text.len() {
// for (int index = 0; index < text.length; index++) {
let pairCode;
let nextChar = if index + 1 < self.text.len() {
self.text[index + 1]
} else {
0
};
pairCode = match self.text[index] {
b'\r' if nextChar == b'\n' => 2,
b'.' if nextChar == b' ' => 3,
b',' if nextChar == b' ' => 4,
b':' if nextChar == b' ' => 5,
_ => 0,
};
// switch (text[index]) {
// case '\r':
// pairCode = nextChar == '\n' ? 2 : 0;
// break;
// case '.' :
// pairCode = nextChar == ' ' ? 3 : 0;
// break;
// case ',' :
// pairCode = nextChar == ' ' ? 4 : 0;
// break;
// case ':' :
// pairCode = nextChar == ' ' ? 5 : 0;
// break;
// default:
// pairCode = 0;
// }
if pairCode > 0 {
// We have one of the four special PUNCT pairs. Treat them specially.
// Get a new set of states for the two new characters.
states = Self::updateStateListForPair(states, index as u32, pairCode);
index += 1;
} else {
// Get a new set of states for the new character.
states = self.updateStateListForChar(states, index as u32);
}
index += 1;
}
// We are left with a set of states. Find the shortest one.
let minState = states
.into_iter()
.min_by(|a, b| {
let diff: i64 = a.getBitCount() as i64 - b.getBitCount() as i64;
if diff < 0 {
Ordering::Less
} else if diff == 0 {
Ordering::Equal
} else {
Ordering::Greater
}
// a.getBitCount() - b.getBitCount()
})
.unwrap();
// let minState = Collections.min(states, new Comparator<State>() {
// @Override
// public int compare(State a, State b) {
// return a.getBitCount() - b.getBitCount();
// }
// });
// Convert it to a bit array, and return.
Ok(minState.toBitArray(&self.text))
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
fn updateStateListForChar(&self, states: Vec<State>, index: u32) -> Vec<State> {
let mut result = Vec::new();
for state in states {
// for (State state : states) {
self.updateStateForChar(state, index, &mut result);
}
Self::simplifyStates(result)
}
// Return a set of states that represent the possible ways of updating this
// state for the next character. The resulting set of states are added to
// the "result" list.
fn updateStateForChar(&self, state: State, index: u32, result: &mut Vec<State>) {
let ch = self.text[index as usize];
let charInCurrentTable = Self::CHAR_MAP[state.getMode() as usize][ch as usize] > 0;
let mut stateNoBinary = None;
for mode in 0..Self::MODE_PUNCT {
// for (int mode = 0; mode <= MODE_PUNCT; mode++) {
let charInMode = Self::CHAR_MAP[mode as usize][ch as usize];
if charInMode > 0 {
if stateNoBinary.is_none() {
// Only create stateNoBinary the first time it's required.
stateNoBinary = Some(state.clone().endBinaryShift(index));
}
// Try generating the character by latching to its mode
if !charInCurrentTable || mode as u32 == state.getMode() || mode == Self::MODE_DIGIT
{
// If the character is in the current table, we don't want to latch to
// any other mode except possibly digit (which uses only 4 bits). Any
// other latch would be equally successful *after* this character, and
// so wouldn't save any bits.
let latchState = stateNoBinary
.clone()
.unwrap()
.latchAndAppend(mode as u32, charInMode as u32);
result.push(latchState);
}
// Try generating the character by switching to its mode.
if !charInCurrentTable && Self::SHIFT_TABLE[state.getMode() as usize][mode] >= 0 {
// It never makes sense to temporarily shift to another mode if the
// character exists in the current mode. That can never save bits.
let shiftState = stateNoBinary
.clone()
.unwrap()
.shiftAndAppend(mode as u32, charInMode as u32);
result.push(shiftState);
}
}
}
if state.getBinaryShiftByteCount() > 0
|| Self::CHAR_MAP[state.getMode() as usize][ch as usize] == 0
{
// It's never worthwhile to go into binary shift mode if you're not already
// in binary shift mode, and the character exists in your current mode.
// That can never save bits over just outputting the char in the current mode.
let binaryState = state.addBinaryShiftChar(index);
result.push(binaryState);
}
}
fn updateStateListForPair(states: Vec<State>, index: u32, pairCode: u32) -> Vec<State> {
let mut result = Vec::new();
for state in states {
// for (State state : states) {
Self::updateStateForPair(state, index, pairCode, &mut result);
}
Self::simplifyStates(result)
}
fn updateStateForPair(state: State, index: u32, pairCode: u32, result: &mut Vec<State>) {
let stateNoBinary = state.clone().endBinaryShift(index);
// Possibility 1. Latch to MODE_PUNCT, and then append this code
result.push(
stateNoBinary
.clone()
.latchAndAppend(Self::MODE_PUNCT as u32, pairCode),
);
if state.getMode() != Self::MODE_PUNCT as u32 {
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
// Every state except MODE_PUNCT (handled above) can shift
result.push(
stateNoBinary
.clone()
.shiftAndAppend(Self::MODE_PUNCT as u32, pairCode),
);
}
if pairCode == 3 || pairCode == 4 {
// both characters are in DIGITS. Sometimes better to just add two digits
let digitState = stateNoBinary
.latchAndAppend(Self::MODE_DIGIT as u32, 16 - pairCode) // period or comma in DIGIT
.latchAndAppend(Self::MODE_DIGIT as u32, 1); // space in DIGIT
result.push(digitState);
}
if state.getBinaryShiftByteCount() > 0 {
// It only makes sense to do the characters as binary if we're already
// in binary mode.
let binaryState = state
.addBinaryShiftChar(index)
.addBinaryShiftChar(index + 1);
result.push(binaryState);
}
}
fn simplifyStates(states: Vec<State>) -> Vec<State> {
let mut result: Vec<State> = Vec::new();
for newState in states {
// for (State newState : states) {
let mut add = true;
for i in 0..result.len() {
// for st in result {
// for (Iterator<State> iterator = result.iterator(); iterator.hasNext();) {
let oldState = result.get(i).unwrap();
if oldState.isBetterThanOrEqualTo(&newState) {
add = false;
break;
}
if newState.isBetterThanOrEqualTo(&oldState) {
result.remove(i);
}
}
if add {
result.push(newState);
}
}
return result;
}
}

View File

@@ -5,6 +5,8 @@ mod binary_shift_token;
mod state;
mod high_level_encoder;
pub mod encoder;
pub use aztec_code::*;
pub use token::*;
pub use simple_token::*;

View File

@@ -18,7 +18,7 @@ use std::fmt;
use crate::common::BitArray;
#[derive(Debug,PartialEq, Eq,Clone)]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct SimpleToken {
// For normal words, indicates value and bitCount
value: u16,
@@ -34,7 +34,9 @@ impl SimpleToken {
}
pub fn appendTo(&self, bitArray: &mut BitArray, text: &[u8]) {
bitArray.appendBits(self.value as u32, self.bitCount as usize).expect("append should never fail");
bitArray
.appendBits(self.value as u32, self.bitCount as usize)
.expect("append should never fail");
}
// @Override

View File

@@ -16,197 +16,241 @@
use std::fmt;
use crate::{exceptions::Exceptions, common::BitArray};
use encoding::Encoding;
use super::{Token, TokenType};
use crate::{common::BitArray, exceptions::Exceptions};
use super::{HighLevelEncoder, Token};
/**
* State represents all information about a sequence necessary to generate the current output.
* Note that a state is immutable.
*/
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct State {
// static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0);
// static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0);
// The current mode of the encoding (or the mode to which we'll return if
// we're in Binary Shift mode.
mode:u32,
// The list of tokens that we output. If we are in Binary Shift mode, this
// token list does *not* yet included the token for those bytes
token:Token,
// If non-zero, the number of most recent bytes that should be output
// in Binary Shift mode.
binaryShiftByteCount:u32,
// The total number of bits generated (including Binary Shift).
bitCount:u32,
binaryShiftCost:u32,
// The current mode of the encoding (or the mode to which we'll return if
// we're in Binary Shift mode.
mode: u32,
// The list of tokens that we output. If we are in Binary Shift mode, this
// token list does *not* yet included the token for those bytes
token: Token,
// If non-zero, the number of most recent bytes that should be output
// in Binary Shift mode.
binaryShiftByteCount: u32,
// The total number of bits generated (including Binary Shift).
bitCount: u32,
binaryShiftCost: u32,
}
impl State {
pub fn new( token:Token, mode:u32, binaryBytes:u32, bitCount:u32) -> Self{
Self{
mode,
token,
binaryShiftByteCount: binaryBytes,
bitCount,
binaryShiftCost: Self::calculateBinaryShiftCost(binaryBytes),
impl State {
pub fn new(token: Token, mode: u32, binaryBytes: u32, bitCount: u32) -> Self {
Self {
mode,
token,
binaryShiftByteCount: binaryBytes,
bitCount,
binaryShiftCost: Self::calculateBinaryShiftCost(binaryBytes),
}
}
}
pub fn getMode(&self) -> u32{
self.mode
}
pub fn getToken(&self) -> &Token{
&self.token
}
pub fn getBinaryShiftByteCount(&self) -> u32{
self.binaryShiftByteCount
}
pub fn getBitCount(&self) -> u32{
self.bitCount
}
pub fn appendFLGn(&self, eci:u32) -> Result<Self,Exceptions> {
let result = self.shiftAndAppend(HighLevelEncoder::MODE_PUNCT, 0); // 0: FLG(n)
let token = result.token;
let bitsAdded = 3;
if eci < 0 {
token.add(0, 3); // 0: FNC1
} else if eci > 999999 {
return Err(Exceptions::IllegalArgumentException("ECI code must be between 0 and 999999".to_owned()));
// throw new IllegalArgumentException("ECI code must be between 0 and 999999");
} else {
let eciDigits = Integer.toString(eci).getBytes(StandardCharsets.ISO_8859_1);
token.add(eciDigits.length, 3); // 1-6: number of ECI digits
for eciDigit in eciDigits {
// for (byte eciDigit : eciDigits) {
token.add(eciDigit - '0' + 2, 4);
}
bitsAdded += eciDigits.length * 4;
pub fn getMode(&self) -> u32 {
self.mode
}
Ok(State::new(token, self.mode, 0, self.bitCount + bitsAdded))
// return new State(token, mode, 0, bitCount + bitsAdded);
}
// Create a new state representing this state with a latch to a (not
// necessary different) mode, and then a code.
pub fn latchAndAppend(&self, mode:u32, value:u32) -> State{
let bitCount = self.bitCount;
let token = self.token;
if mode != self.mode {
let latch = HighLevelEncoder.LATCH_TABLE[this.mode][mode];
token.add(latch & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
pub fn getToken(&self) -> &Token {
&self.token
}
let latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
token.add(value, latchModeBitCount);
State::new(token, mode, 0, bitCount + latchModeBitCount)
}
// Create a new state representing this state, with a temporary shift
// to a different mode to output a single value.
pub fn shiftAndAppend(&self, mode:u32, value:u32) -> State{
let token = this.token;
let thisModeBitCount = this.mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
token = token.add(HighLevelEncoder.SHIFT_TABLE[this.mode][mode], thisModeBitCount);
token = token.add(value, 5);
State::new(token, this.mode, 0, this.bitCount + thisModeBitCount + 5)
}
pub fn getBinaryShiftByteCount(&self) -> u32 {
self.binaryShiftByteCount
}
// Create a new state representing this state, but an additional character
// output in Binary Shift mode.
pub fn addBinaryShiftChar(&self, index:u32) -> State{
let token = this.token;
let mode = this.mode;
let bitCount = this.bitCount;
if self.mode == HighLevelEncoder.MODE_PUNCT || self.mode == HighLevelEncoder.MODE_DIGIT {
let latch = HighLevelEncoder.LATCH_TABLE[mode][HighLevelEncoder.MODE_UPPER];
token.add(latch & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
mode = HighLevelEncoder.MODE_UPPER;
pub fn getBitCount(&self) -> u32 {
self.bitCount
}
let deltaBitCount =
(binaryShiftByteCount == 0 || binaryShiftByteCount == 31) ? 18 :
(binaryShiftByteCount == 62) ? 9 : 8;
let result = State::new(token, mode, binaryShiftByteCount + 1, bitCount + deltaBitCount);
if (result.binaryShiftByteCount == 2047 + 31) {
// The string is as long as it's allowed to be. We should end it.
result = result.endBinaryShift(index + 1);
}
result
}
// Create the state identical to this one, but we are no longer in
// Binary Shift mode.
pub fn endBinaryShift(self, index:u32) -> State{
if self.binaryShiftByteCount == 0 {
return self;
pub fn appendFLGn(self, eci: u32) -> Result<Self, Exceptions> {
let bit_count = self.bitCount;
let mode = self.mode;
let result = self.shiftAndAppend(HighLevelEncoder::MODE_PUNCT as u32, 0); // 0: FLG(n)
let mut token = result.token;
let mut bitsAdded = 3;
if eci < 0 {
token.add(0, 3); // 0: FNC1
} else if eci > 999999 {
return Err(Exceptions::IllegalArgumentException(
"ECI code must be between 0 and 999999".to_owned(),
));
// throw new IllegalArgumentException("ECI code must be between 0 and 999999");
} else {
let eciDigits = encoding::all::ISO_8859_1
.encode(&format!("{}", eci), encoding::EncoderTrap::Replace)
.unwrap();
// let eciDigits = Integer.toString(eci).getBytes(StandardCharsets.ISO_8859_1);
token.add(eciDigits.len() as i32, 3); // 1-6: number of ECI digits
for eciDigit in &eciDigits {
// for (byte eciDigit : eciDigits) {
token.add((eciDigit - b'0' + 2) as i32, 4);
}
bitsAdded += eciDigits.len() * 4;
}
Ok(State::new(token, mode, 0, bit_count + bitsAdded as u32))
// return new State(token, mode, 0, bitCount + bitsAdded);
}
let token = self.token;
self.token.addBinaryShift(index - self.binaryShiftByteCount, self.binaryShiftByteCount);
State::new(token, self.mode, 0, self.bitCount)
}
// Returns true if "this" state is better (or equal) to be in than "that"
// state under all possible circumstances.
pub fn isBetterThanOrEqualTo(&self, other:&State)->bool {
let newModeBitCount = self.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16);
if self.binaryShiftByteCount < other.binaryShiftByteCount {
// add additional B/S encoding cost of other, if any
newModeBitCount += other.binaryShiftCost - self.binaryShiftCost;
} else if self.binaryShiftByteCount > other.binaryShiftByteCount && other.binaryShiftByteCount > 0 {
// maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it)
newModeBitCount += 10;
}
newModeBitCount <= other.bitCount
}
// Create a new state representing this state with a latch to a (not
// necessary different) mode, and then a code.
pub fn latchAndAppend(self, mode: u32, value: u32) -> State {
let mut bitCount = self.bitCount;
let mut token = self.token;
if mode != self.mode {
let latch = HighLevelEncoder::LATCH_TABLE[self.mode as usize][mode as usize];
token.add(latch as i32 & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
}
let latchModeBitCount = if mode == HighLevelEncoder::MODE_DIGIT as u32 {
4
} else {
5
};
token.add(value as i32, latchModeBitCount);
pub fn toBitArray(&self, text:&[u8]) -> BitArray{
let symbols = Vec::new();
let mut tok = self.endBinaryShift(text.len() as u32).token;
let mut tkn = tok.getPrevious();
while tkn != &TokenType::Empty {
// for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {
symbols.push(tkn);
tkn = tok.getPrevious();
State::new(token, mode, 0, bitCount + latchModeBitCount)
}
let bitArray = BitArray::new();
// Add each token to the result in forward order
for i in (0..symbols.len()-1).rev() {
// for (int i = symbols.size() - 1; i >= 0; i--) {
symbols.get(i).unwrap().appendTo(bitArray, text);
}
bitArray
}
// @Override
// public String toString() {
// return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);
// }
// Create a new state representing this state, with a temporary shift
// to a different mode to output a single value.
pub fn shiftAndAppend(self, mode: u32, value: u32) -> State {
let mut token = self.token;
let thisModeBitCount = if self.mode == HighLevelEncoder::MODE_DIGIT as u32 {
4
} else {
5
};
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
token.add(
HighLevelEncoder::SHIFT_TABLE[self.mode as usize][mode as usize],
thisModeBitCount,
);
token.add(value as i32, 5);
State::new(token, self.mode, 0, self.bitCount + thisModeBitCount + 5)
}
fn calculateBinaryShiftCost( binaryShiftByteCount:u32) -> u32{
if binaryShiftByteCount > 62 {
return 21; // B/S with extended length
// Create a new state representing this state, but an additional character
// output in Binary Shift mode.
pub fn addBinaryShiftChar(self, index: u32) -> State {
let mut token = self.token;
let mut mode = self.mode;
let mut bitCount = self.bitCount;
if self.mode == HighLevelEncoder::MODE_PUNCT as u32
|| self.mode == HighLevelEncoder::MODE_DIGIT as u32
{
let latch = HighLevelEncoder::LATCH_TABLE[mode as usize][HighLevelEncoder::MODE_UPPER];
token.add(latch as i32 & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
mode = HighLevelEncoder::MODE_UPPER as u32;
}
let deltaBitCount = if self.binaryShiftByteCount == 0 || self.binaryShiftByteCount == 31 {
18
} else {
if self.binaryShiftByteCount == 62 {
9
} else {
8
}
};
let mut result = State::new(
token,
mode,
self.binaryShiftByteCount + 1,
bitCount + deltaBitCount,
);
if result.binaryShiftByteCount == 2047 + 31 {
// The string is as long as it's allowed to be. We should end it.
result = result.endBinaryShift(index + 1);
}
result
}
if binaryShiftByteCount > 31 {
return 20; // two B/S
}
if binaryShiftByteCount > 0 {
return 10; // one B/S
}
return 0;
}
// Create the state identical to this one, but we are no longer in
// Binary Shift mode.
pub fn endBinaryShift(self, index: u32) -> State {
if self.binaryShiftByteCount == 0 {
return self;
}
let mut token = self.token;
token.addBinaryShift(index - self.binaryShiftByteCount, self.binaryShiftByteCount);
State::new(token, self.mode, 0, self.bitCount)
}
// Returns true if "this" state is better (or equal) to be in than "that"
// state under all possible circumstances.
pub fn isBetterThanOrEqualTo(&self, other: &State) -> bool {
let mut newModeBitCount = self.bitCount
+ (HighLevelEncoder::LATCH_TABLE[self.mode as usize][other.mode as usize] >> 16);
if self.binaryShiftByteCount < other.binaryShiftByteCount {
// add additional B/S encoding cost of other, if any
newModeBitCount += other.binaryShiftCost - self.binaryShiftCost;
} else if self.binaryShiftByteCount > other.binaryShiftByteCount
&& other.binaryShiftByteCount > 0
{
// maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it)
newModeBitCount += 10;
}
newModeBitCount <= other.bitCount
}
pub fn toBitArray(self, text: &[u8]) -> BitArray {
let mut symbols = Vec::new();
let tok = self.endBinaryShift(text.len() as u32).token;
for tkn in tok.into_iter() {
// for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {
symbols.push(tkn);
}
// let mut tkn = tok.getPrevious();
// while tkn != &TokenType::Empty {
// // for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {
// symbols.push(tkn);
// tkn = tok.getPrevious();
// }
let mut bitArray = BitArray::new();
// Add each token to the result in forward order
for i in (0..symbols.len() - 1).rev() {
// for (int i = symbols.size() - 1; i >= 0; i--) {
symbols.get(i).unwrap().appendTo(&mut bitArray, text);
}
bitArray
}
// @Override
// public String toString() {
// return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);
// }
fn calculateBinaryShiftCost(binaryShiftByteCount: u32) -> u32 {
if binaryShiftByteCount > 62 {
return 21; // B/S with extended length
}
if binaryShiftByteCount > 31 {
return 20; // two B/S
}
if binaryShiftByteCount > 0 {
return 10; // one B/S
}
return 0;
}
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,"{} bits={} bytes={}", HighLevelEncoder::MODE_NAMES[mode], self.bitCount, self.binaryShiftByteCount)
write!(
f,
"{} bits={} bytes={}",
HighLevelEncoder::MODE_NAMES[self.mode as usize],
self.bitCount,
self.binaryShiftByteCount
)
}
}
}

View File

@@ -18,39 +18,83 @@ use std::rc::Rc;
use crate::common::BitArray;
use super::{SimpleToken, BinaryShiftToken};
use super::{BinaryShiftToken, SimpleToken};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TokenType {
Simple(SimpleToken), BinaryShift(BinaryShiftToken), Empty
Simple(SimpleToken),
BinaryShift(BinaryShiftToken),
Empty,
}
impl TokenType {
pub fn appendTo(&self, bit_array: &mut BitArray, text: &[u8]) {
// let token = &self.tokens[self.current_pointer];
match self {
TokenType::Simple(a) => a.appendTo(bit_array, text),
TokenType::BinaryShift(a) => a.appendTo(bit_array, text),
TokenType::Empty => panic!("cannot appendTo on Empty final item"),
}
}
}
#[derive(Debug,Clone,PartialEq, Eq)]
pub struct Token {
tokens: Vec<TokenType>,
current_pointer: usize,
tokens: Vec<TokenType>,
//current_pointer: usize,
}
impl Token {
pub fn new () -> Self {
Self {
tokens: vec![TokenType::Empty],
current_pointer: 0,
pub fn new() -> Self {
Self {
tokens: Vec::new(),
//current_pointer: 0,
}
}
// pub fn getPrevious(&mut self) -> &TokenType {
// self.current_pointer -= 1;
// &self.tokens[self.current_pointer]
// }
pub fn add(&mut self, value: i32, bit_count: u32) {
//self.current_pointer += 1;
self.tokens
.push(TokenType::Simple(SimpleToken::new(value, bit_count)));
// &self.tokens[self.current_pointer]
}
pub fn addBinaryShift(&mut self, start: u32, byte_count: u32) {
//self.current_pointer += 1;
self.tokens
.push(TokenType::BinaryShift(BinaryShiftToken::new(
start, byte_count,
)));
// &self.tokens[self.current_pointer]
}
}
pub struct TokenIter {
src: Vec<TokenType>,
// pos: usize,
}
impl Iterator for TokenIter {
type Item = TokenType;
fn next(&mut self) -> Option<Self::Item> {
self.src.pop()
}
}
impl IntoIterator for Token {
type Item = TokenType;
type IntoIter = TokenIter;
fn into_iter(self) -> Self::IntoIter {
TokenIter {
src: self.tokens,
// pos: self.current_pointer,
}
}
}
pub fn getPrevious(&mut self) -> &TokenType{
self.current_pointer -= 1;
&self.tokens[self.current_pointer]
}
pub fn add(&mut self, value:i32, bit_count: u32,) -> &TokenType {
self.current_pointer+=1;
self.tokens.push(TokenType::Simple(SimpleToken::new(value,bit_count)));
&self.tokens[self.current_pointer]
}
pub fn addBinaryShift(&mut self, start: u32, byte_count: u32) -> &TokenType {
self.current_pointer+=1;
self.tokens.push(TokenType::BinaryShift(BinaryShiftToken::new(start,byte_count)));
&self.tokens[self.current_pointer]
}
}
// pub enum Token{

View File

@@ -1,13 +1,18 @@
mod AztecDetectorResult;
mod aztec_reader;
mod aztec_writer;
pub mod decoder;
pub mod detector;
pub mod encoder;
pub use aztec_reader::*;
pub use aztec_writer::*;
#[cfg(test)]
mod DecoderTest;
// #[cfg(test)]
// mod EncoderTest;
// #[cfg(test)]
// mod DetectorTest;
#[cfg(test)]
mod EncoderTest;
#[cfg(test)]
mod DetectorTest;
mod shared_test_methods;

View File

@@ -770,7 +770,7 @@ pub trait DetectorRXingResult {
pub struct BitMatrix {
width: u32,
height: u32,
rowSize: usize,
row_size: usize,
bits: Vec<u32>,
}
@@ -799,7 +799,7 @@ impl BitMatrix {
Ok(Self {
width,
height,
rowSize: ((width + 31) / 32) as usize,
row_size: ((width + 31) / 32) as usize,
bits: vec![0; (((width + 31) / 32) * height) as usize],
})
// this.width = width;
@@ -812,7 +812,7 @@ impl BitMatrix {
Self {
width,
height,
rowSize,
row_size: rowSize,
bits,
}
}
@@ -926,7 +926,7 @@ impl BitMatrix {
* @return value of given bit in matrix
*/
pub fn get(&self, x: u32, y: u32) -> bool {
let offset = y as usize * self.rowSize + (x as usize / 32);
let offset = y as usize * self.row_size + (x as usize / 32);
return ((self.bits[offset] >> (x & 0x1f)) & 1) != 0;
}
@@ -937,12 +937,12 @@ impl BitMatrix {
* @param y The vertical component (i.e. which row)
*/
pub fn set(&mut self, x: u32, y: u32) {
let offset = y as usize * self.rowSize + (x as usize / 32);
let offset = y as usize * self.row_size + (x as usize / 32);
self.bits[offset] |= 1 << (x & 0x1f);
}
pub fn unset(&mut self, x: u32, y: u32) {
let offset = y as usize * self.rowSize + (x as usize / 32);
let offset = y as usize * self.row_size + (x as usize / 32);
self.bits[offset] &= !(1 << (x & 0x1f));
}
@@ -953,7 +953,7 @@ impl BitMatrix {
* @param y The vertical component (i.e. which row)
*/
pub fn flip_coords(&mut self, x: u32, y: u32) {
let offset = y as usize * self.rowSize + (x as usize / 32);
let offset = y as usize * self.row_size + (x as usize / 32);
self.bits[offset] ^= 1 << (x & 0x1f);
}
@@ -975,7 +975,7 @@ impl BitMatrix {
* @param mask XOR mask
*/
pub fn xor(&mut self, mask: &BitMatrix) -> Result<(), Exceptions> {
if self.width != mask.width || self.height != mask.height || self.rowSize != mask.rowSize {
if self.width != mask.width || self.height != mask.height || self.row_size != mask.row_size {
return Err(Exceptions::IllegalArgumentException(
"input matrix dimensions do not match".to_owned(),
));
@@ -983,10 +983,10 @@ impl BitMatrix {
let rowArray = BitArray::with_size(self.width as usize);
for y in 0..self.height {
//for (int y = 0; y < height; y++) {
let offset = y as usize * self.rowSize;
let offset = y as usize * self.row_size;
let tmp = mask.getRow(y, &rowArray);
let row = tmp.getBitArray();
for x in 0..self.rowSize {
for x in 0..self.row_size {
//for (int x = 0; x < rowSize; x++) {
self.bits[offset + x] ^= row[x];
}
@@ -1039,7 +1039,7 @@ impl BitMatrix {
}
for y in top..bottom {
//for (int y = top; y < bottom; y++) {
let offset = y as usize * self.rowSize;
let offset = y as usize * self.row_size;
for x in left..right {
//for (int x = left; x < right; x++) {
self.bits[offset + (x as usize / 32)] |= 1 << (x & 0x1f);
@@ -1067,8 +1067,8 @@ impl BitMatrix {
// row.clone()
};
let offset = y as usize * self.rowSize;
for x in 0..self.rowSize {
let offset = y as usize * self.row_size;
for x in 0..self.row_size {
//for (int x = 0; x < rowSize; x++) {
rw.setBulk(x * 32, self.bits[offset + x]);
}
@@ -1080,8 +1080,8 @@ impl BitMatrix {
* @param row {@link BitArray} to copy from
*/
pub fn setRow(&mut self, y: u32, row: &BitArray) {
return self.bits[y as usize * self.rowSize..y as usize * self.rowSize + self.rowSize]
.clone_from_slice(&row.getBitArray()[0..self.rowSize]);
return self.bits[y as usize * self.row_size..y as usize * self.row_size + self.row_size]
.clone_from_slice(&row.getBitArray()[0..self.row_size]);
//System.arraycopy(row.getBitArray(), 0, self.bits, y * self.rowSize, self.rowSize);
}
@@ -1144,7 +1144,7 @@ impl BitMatrix {
//for (int y = 0; y < height; y++) {
for x in 0..self.width {
//for (int x = 0; x < width; x++) {
let offset = y as usize * self.rowSize + (x as usize / 32);
let offset = y as usize * self.row_size + (x as usize / 32);
if ((self.bits[offset] >> (x & 0x1f)) & 1) != 0 {
let newOffset: usize = ((newHeight - 1 - x) * newRowSize + (y / 32))
.try_into()
@@ -1155,7 +1155,7 @@ impl BitMatrix {
}
self.width = newWidth;
self.height = newHeight;
self.rowSize = newRowSize.try_into().unwrap();
self.row_size = newRowSize.try_into().unwrap();
self.bits = newBits;
}
@@ -1174,9 +1174,9 @@ impl BitMatrix {
for y in 0..self.height {
//for (int y = 0; y < height; y++) {
for x32 in 0..self.rowSize {
for x32 in 0..self.row_size {
//for (int x32 = 0; x32 < rowSize; x32++) {
let theBits = self.bits[y as usize * self.rowSize + x32];
let theBits = self.bits[y as usize * self.row_size + x32];
if theBits != 0 {
if y < top {
top = y;
@@ -1226,8 +1226,8 @@ impl BitMatrix {
if bitsOffset == self.bits.len() {
return None;
}
let y = bitsOffset / self.rowSize;
let mut x = (bitsOffset % self.rowSize) * 32;
let y = bitsOffset / self.row_size;
let mut x = (bitsOffset % self.row_size) * 32;
let theBits = self.bits[bitsOffset];
let mut bit = 0;
@@ -1247,8 +1247,8 @@ impl BitMatrix {
return None;
}
let y = bitsOffset as usize / self.rowSize;
let mut x = (bitsOffset as usize % self.rowSize) * 32;
let y = bitsOffset as usize / self.row_size;
let mut x = (bitsOffset as usize % self.row_size) * 32;
let theBits = self.bits[bitsOffset as usize];
let mut bit = 31;
@@ -1278,7 +1278,7 @@ impl BitMatrix {
* @return The row size of the matrix
*/
pub fn getRowSize(&self) -> usize {
return self.rowSize;
return self.row_size;
}
// @Override

View File

@@ -1,12 +1,12 @@
mod aztec;
mod client;
mod common;
mod exceptions;
mod client;
mod aztec;
#[cfg(feature="image")]
#[cfg(feature = "image")]
mod BufferedImageLuminanceSource;
#[cfg(feature="image")]
#[cfg(feature = "image")]
pub use BufferedImageLuminanceSource::*;
use crate::common::{BitArray, BitMatrix};
@@ -45,8 +45,8 @@ mod RGBLuminanceSourceTestCase;
*
* @author Sean Owen
*/
#[derive(Debug,PartialEq, Eq,Hash)]
pub enum BarcodeFormat {
#[derive(Debug, PartialEq, Eq, Hash,Clone, Copy)]
pub enum BarcodeFormat {
/** Aztec 2D barcode format. */
AZTEC,
@@ -122,6 +122,7 @@ mod RGBLuminanceSourceTestCase;
*
* @author dswitkin@google.com (Daniel Switkin)
*/
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum EncodeHintType {
/**
* Specifies what degree of error correction to use, for example in QR Codes.
@@ -274,6 +275,158 @@ pub enum EncodeHintType {
CODE128_COMPACT,
}
pub enum EncodeHintValue {
/**
* Specifies what degree of error correction to use, for example in QR Codes.
* Type depends on the encoder. For example for QR codes it's type
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
* For PDF417 it is of type {@link Integer}, valid values being 0 to 8.
* In all cases, it can also be a {@link String} representation of the desired value as well.
* Note: an Aztec symbol should have a minimum of 25% EC words.
*/
ErrorCorrection(String),
/**
* Specifies what character encoding to use where applicable (type {@link String})
*/
CharacterSet(String),
/**
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
*/
DataMatrixShape,
/**
* Specifies whether to use compact mode for Data Matrix (type {@link Boolean}, or "true" or "false"
* {@link String } value).
* The compact encoding mode also supports the encoding of characters that are not in the ISO-8859-1
* character set via ECIs.
* Please note that in that case, the most compact character encoding is chosen for characters in
* the input that are not in the ISO-8859-1 character set. Based on experience, some scanners do not
* support encodings like cp-1256 (Arabic). In such cases the encoding can be forced to UTF-8 by
* means of the {@link #CHARACTER_SET} encoding hint.
* Compact encoding also provides GS1-FNC1 support when {@link #GS1_FORMAT} is selected. In this case
* group-separator character (ASCII 29 decimal) can be used to encode the positions of FNC1 codewords
* for the purpose of delimiting AIs.
* This option and {@link #FORCE_C40} are mutually exclusive.
*/
DataMatrixCompact(String),
/**
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
*
* @deprecated use width/height params in
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
*/
#[deprecated]
MinSize,
/**
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
*
* @deprecated without replacement
*/
#[deprecated]
MaxSize(Dimension),
/**
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
* by format; for example it controls margin before and after the barcode horizontally for
* most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).
*/
Margin(String),
/**
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false"
* {@link String} value).
*/
Pdf417Compact(String),
/**
* Specifies what compaction mode to use for PDF417 (type
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its
* enum values).
*/
Pdf417Compaction(String),
/**
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
*/
Pdf417Dimensions,
/**
* Specifies whether to automatically insert ECIs when encoding PDF417 (type {@link Boolean}, or "true" or "false"
* {@link String} value).
* Please note that in that case, the most compact character encoding is chosen for characters in
* the input that are not in the ISO-8859-1 character set. Based on experience, some scanners do not
* support encodings like cp-1256 (Arabic). In such cases the encoding can be forced to UTF-8 by
* means of the {@link #CHARACTER_SET} encoding hint.
*/
Pdf417AutoEci(String),
/**
* Specifies the required number of layers for an Aztec code.
* A negative number (-1, -2, -3, -4) specifies a compact Aztec code.
* 0 indicates to use the minimum number of layers (the default).
* A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.
* (Type {@link Integer}, or {@link String} representation of the integer value).
*/
AztecLayers(u32),
/**
* Specifies the exact version of QR code to be encoded.
* (Type {@link Integer}, or {@link String} representation of the integer value).
*/
QrVersion(String),
/**
* Specifies the QR code mask pattern to be used. Allowed values are
* 0..QRCode.NUM_MASK_PATTERNS-1. By default the code will automatically select
* the optimal mask pattern.
* * (Type {@link Integer}, or {@link String} representation of the integer value).
*/
QrMaskPattern(String),
/**
* Specifies whether to use compact mode for QR code (type {@link Boolean}, or "true" or "false"
* {@link String } value).
* Please note that when compaction is performed, the most compact character encoding is chosen
* for characters in the input that are not in the ISO-8859-1 character set. Based on experience,
* some scanners do not support encodings like cp-1256 (Arabic). In such cases the encoding can
* be forced to UTF-8 by means of the {@link #CHARACTER_SET} encoding hint.
*/
QrCompact(String),
/**
* Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or "true" or "false"
* {@link String } value).
*/
Gs1Format(String),
/**
* Forces which encoding will be used. Currently only used for Code-128 code sets (Type {@link String}).
* Valid values are "A", "B", "C".
* This option and {@link #CODE128_COMPACT} are mutually exclusive.
*/
ForceCodeSet(String),
/**
* Forces C40 encoding for data-matrix (type {@link Boolean}, or "true" or "false") {@link String } value). This
* option and {@link #DATA_MATRIX_COMPACT} are mutually exclusive.
*/
ForceC40(String),
/**
* Specifies whether to use compact mode for Code-128 code (type {@link Boolean}, or "true" or "false"
* {@link String } value).
* This can yield slightly smaller bar codes. This option and {@link #FORCE_CODE_SET} are mutually
* exclusive.
*/
Code128Compact(String),
}
/*
* Copyright 2007 ZXing authors
*
@@ -398,6 +551,91 @@ pub enum DecodeHintType {
}*/
}
/**
* Callback which is invoked when a possible result point (significant
* point in the barcode image such as a corner) is found.
*
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
*/
pub type RXingResultPointCallback = fn(&RXingResultPoint);
pub enum DecodeHintValue {
/**
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
*/
Other(String),
/**
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
* use {@link Boolean#TRUE}.
*/
PureBarcode(bool),
/**
* Image is known to be of one of a few possible formats.
* Maps to a {@link List} of {@link BarcodeFormat}s.
*/
PossibleFormats(BarcodeFormat),
/**
* Spend more time to try to find a barcode; optimize for accuracy, not speed.
* Doesn't matter what it maps to; use {@link Boolean#TRUE}.
*/
TryHarder(bool),
/**
* Specifies what character encoding to use when decoding, where applicable (type String)
*/
CharacterSet(String),
/**
* Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.
*/
AllowedLengths(Vec<u32>),
/**
* Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;
* use {@link Boolean#TRUE}.
*/
AssumeCode39CheckDigit(bool),
/**
* Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
* For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
* use {@link Boolean#TRUE}.
*/
AssumeGs1(bool),
/**
* If true, return the start and end digits in a Codabar barcode instead of stripping them. They
* are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
* to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
*/
ReturnCodabarStartEnd(bool),
/**
* The caller needs to be notified via callback when a possible {@link RXingResultPoint}
* is found. Maps to a {@link RXingResultPointCallback}.
*/
NeedResultPointCallback(RXingResultPointCallback),
/**
* Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
* Maps to an {@code int[]} of the allowed extension lengths, for example [2], [5], or [2, 5].
* If it is optional to have an extension, do not set this hint. If this is set,
* and a UPC or EAN barcode is found but an extension is not, then no result will be returned
* at all.
*/
AllowedEanExtensions(Vec<u32>),
/**
* If true, also tries to decode as inverted image. All configured decoders are simply called a
* second time with an inverted image. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
*/
AlsoInverted(bool),
// End of enumeration values.
}
/*
* Copyright 2008 ZXing authors
*
@@ -448,12 +686,12 @@ pub trait Writer {
* @return {@link BitMatrix} representing encoded barcode image
* @throws WriterException if contents cannot be encoded legally in a format
*/
fn encode_with_hints<T>(
fn encode_with_hints(
contents: &str,
format: &BarcodeFormat,
width: i32,
height: i32,
hints: HashMap<EncodeHintType, T>,
hints: &HashMap<EncodeHintType, EncodeHintValue>,
) -> Result<BitMatrix, Exceptions>;
}
@@ -475,8 +713,6 @@ pub trait Writer {
//package com.google.zxing;
/**
* Implementations of this interface can decode an image of a barcode in some format into
* the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
@@ -499,7 +735,7 @@ pub trait Reader {
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
* @throws FormatException if a potential barcode is found but format is invalid
*/
fn decode(image: BinaryBitmap) -> Result<RXingResult, Exceptions>;
fn decode(image: &BinaryBitmap) -> Result<RXingResult, Exceptions>;
/**
* Locates and decodes a barcode in some format within an image. This method also accepts
@@ -515,9 +751,9 @@ pub trait Reader {
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
* @throws FormatException if a potential barcode is found but format is invalid
*/
fn decode_with_hints<T>(
image: BinaryBitmap,
hints: HashMap<DecodeHintType, T>,
fn decode_with_hints(
image: &BinaryBitmap,
hints: &HashMap<DecodeHintType, DecodeHintValue>,
) -> Result<RXingResult, Exceptions>;
/**
@@ -631,6 +867,85 @@ pub enum RXingResultMetadataType {
SYMBOLOGY_IDENTIFIER,
}
pub enum RXingResultMetadataValue {
/**
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
*/
OTHER,
/**
* Denotes the likely approximate orientation of the barcode in the image. This value
* is given as degrees rotated clockwise from the normal, upright orientation.
* For example a 1D barcode which was found by reading top-to-bottom would be
* said to have orientation "90". This key maps to an {@link Integer} whose
* value is in the range [0,360).
*/
Orientation(i32),
/**
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
* which is sometimes used to encode binary data. While {@link RXingResult} makes available
* the complete raw bytes in the barcode for these formats, it does not offer the bytes
* from the byte segments alone.</p>
*
* <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
* raw bytes in the byte segments in the barcode, in order.</p>
*/
ByteSegments(Vec<u8>),
/**
* Error correction level used, if applicable. The value type depends on the
* format, but is typically a String.
*/
ErrorCorrectionLevel(String),
/**
* For some periodicals, indicates the issue number as an {@link Integer}.
*/
IssueNumber(i32),
/**
* For some products, indicates the suggested retail price in the barcode as a
* formatted {@link String}.
*/
SuggestedPrice(String),
/**
* For some products, the possible country of manufacture as a {@link String} denoting the
* ISO country code. Some map to multiple possible countries, like "US/CA".
*/
PossibleCountry(String),
/**
* For some products, the extension text
*/
UpcEanExtension(String),
/**
* PDF417-specific metadata
*/
Pdf417ExtraMetadata(String),
/**
* If the code format supports structured append and the current scanned code is part of one then the
* sequence number is given with it.
*/
StructuredAppendSequence(u32),
/**
* If the code format supports structured append and the current scanned code is part of one then the
* parity is given with it.
*/
StructuredAppendParity(u32),
/**
* Barcode Symbology Identifier.
* Note: According to the GS1 specification the identifier may have to replace a leading FNC1/GS character
* when prepending to the barcode content.
*/
SymbologyIdentifier(String),
}
/*
* Copyright 2007 ZXing authors
*
@@ -663,7 +978,7 @@ pub struct RXingResult {
numBits: usize,
resultPoints: Vec<RXingResultPoint>,
format: BarcodeFormat,
resultMetadata: HashMap<RXingResultMetadataType, String>,
resultMetadata: HashMap<RXingResultMetadataType, RXingResultMetadataValue>,
timestamp: u128,
}
impl RXingResult {
@@ -758,15 +1073,24 @@ impl RXingResult {
* {@code null}. This contains optional metadata about what was detected about the barcode,
* like orientation.
*/
pub fn getRXingResultMetadata(&self) -> &HashMap<RXingResultMetadataType, String> {
pub fn getRXingResultMetadata(
&self,
) -> &HashMap<RXingResultMetadataType, RXingResultMetadataValue> {
return &self.resultMetadata;
}
pub fn putMetadata(&mut self, md_type: RXingResultMetadataType, value: String) {
pub fn putMetadata(
&mut self,
md_type: RXingResultMetadataType,
value: RXingResultMetadataValue,
) {
self.resultMetadata.insert(md_type, value);
}
pub fn putAllMetadata(&mut self, metadata: HashMap<RXingResultMetadataType, String>) {
pub fn putAllMetadata(
&mut self,
metadata: HashMap<RXingResultMetadataType, RXingResultMetadataValue>,
) {
if self.resultMetadata.is_empty() {
self.resultMetadata = metadata;
} else {
@@ -1248,34 +1572,6 @@ impl fmt::Display for BinaryBitmap {
//package com.google.zxing;
/**
* Callback which is invoked when a possible result point (significant
* point in the barcode image such as a corner) is found.
*
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
*/
pub trait RXingResultPointCallback {
fn foundPossibleRXingResultPoint(point: RXingResultPoint);
}
/*
* 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;
/**
* The purpose of this class hierarchy is to abstract different bitmap implementations across
* platforms into a standard interface for requesting greyscale luminance values. The interface
@@ -1375,9 +1671,7 @@ pub trait LuminanceSource {
*
* @return A rotated version of this object.
*/
fn rotateCounterClockwise(
&self,
) -> Result<Box<dyn LuminanceSource>, Exceptions> {
fn rotateCounterClockwise(&self) -> Result<Box<dyn LuminanceSource>, Exceptions> {
return Err(Exceptions::UnsupportedOperationException(
"This luminance source does not support rotation by 90 degrees.".to_owned(),
));
@@ -1389,9 +1683,7 @@ pub trait LuminanceSource {
*
* @return A rotated version of this object.
*/
fn rotateCounterClockwise45(
&self,
) -> Result<Box<dyn LuminanceSource>, Exceptions> {
fn rotateCounterClockwise45(&self) -> Result<Box<dyn LuminanceSource>, Exceptions> {
return Err(Exceptions::UnsupportedOperationException(
"This luminance source does not support rotation by 45 degrees.".to_owned(),
));
@@ -1624,7 +1916,7 @@ impl PlanarYUVLuminanceSource {
pub fn renderThumbnail(&self) -> Vec<u8> {
let width = self.getWidth() / THUMBNAIL_SCALE_FACTOR;
let height = self.getHeight() / THUMBNAIL_SCALE_FACTOR;
let mut pixels = vec![0;width * height];
let mut pixels = vec![0; width * height];
let yuv = &self.yuv_data;
let mut input_offset = self.top * self.data_width + self.left;
@@ -1692,13 +1984,13 @@ impl LuminanceSource for PlanarYUVLuminanceSource {
let offset = (y + self.top) * self.data_width + self.left;
let mut row = if row.len() >= width{
row.to_vec()}
else{
vec![0;width]
};
let mut row = if row.len() >= width {
row.to_vec()
} else {
vec![0; width]
};
row[..width].clone_from_slice(&self.yuv_data[offset..width+offset]);
row[..width].clone_from_slice(&self.yuv_data[offset..width + offset]);
//System.arraycopy(yuvData, offset, row, 0, width);
if self.invert {
row = self.invert_block_of_bytes(row);
@@ -1721,7 +2013,7 @@ impl LuminanceSource for PlanarYUVLuminanceSource {
}
let area = width * height;
let mut matrix = vec![0;area];
let mut matrix = vec![0; area];
let mut inputOffset = self.top * self.data_width + self.left;
// If the width matches the full width of the underlying data, perform a single copy.
@@ -1738,7 +2030,8 @@ impl LuminanceSource for PlanarYUVLuminanceSource {
for y in 0..height {
//for (int y = 0; y < height; y++) {
let outputOffset = y * width;
matrix[outputOffset..outputOffset+width].clone_from_slice(&self.yuv_data[inputOffset..inputOffset+width]);
matrix[outputOffset..outputOffset + width]
.clone_from_slice(&self.yuv_data[inputOffset..inputOffset + width]);
//System.arraycopy(yuvData, inputOffset, matrix, outputOffset, width);
inputOffset += self.data_width;
}
@@ -1841,12 +2134,12 @@ impl LuminanceSource for RGBLuminanceSource {
let offset = (y + self.top) * self.dataWidth + self.left;
let mut row = if row.len() >= width {
row.to_vec()
}else{
vec![0;width]
row.to_vec()
} else {
vec![0; width]
};
row[..width].clone_from_slice(&self.luminances[offset..offset+width]);
row[..width].clone_from_slice(&self.luminances[offset..offset + width]);
//System.arraycopy(self.luminances, offset, row, 0, width);
if self.invert {
row = self.invert_block_of_bytes(row);
@@ -1869,12 +2162,12 @@ impl LuminanceSource for RGBLuminanceSource {
}
let area = width * height;
let mut matrix = vec![0;area];
let mut matrix = vec![0; area];
let mut inputOffset = self.top * self.dataWidth + self.left;
// If the width matches the full width of the underlying data, perform a single copy.
if width == self.dataWidth {
matrix[..area].clone_from_slice(&self.luminances[inputOffset..area+inputOffset]);
matrix[..area].clone_from_slice(&self.luminances[inputOffset..area + inputOffset]);
//System.arraycopy(self.luminances, inputOffset, matrix, 0, area);
if self.invert {
matrix = self.invert_block_of_bytes(matrix);
@@ -1886,7 +2179,8 @@ impl LuminanceSource for RGBLuminanceSource {
for y in 0..height {
//for (int y = 0; y < height; y++) {
let outputOffset = y * width;
matrix[outputOffset..width+outputOffset].clone_from_slice(&self.luminances[inputOffset..width+inputOffset]);
matrix[outputOffset..width + outputOffset]
.clone_from_slice(&self.luminances[inputOffset..width + inputOffset]);
//System.arraycopy(luminances, inputOffset, matrix, outputOffset, width);
inputOffset += self.dataWidth;
}
@@ -1949,11 +2243,11 @@ impl RGBLuminanceSource {
//
// Total number of pixels suffices, can ignore shape
let size = width * height;
let mut luminances: Vec<u8> = vec![0;size];
let mut luminances: Vec<u8> = vec![0; size];
for offset in 0..size {
//for (int offset = 0; offset < size; offset++) {
let pixel = pixels[offset];
let r = (pixel >> 16) & 0xff ; // red
let r = (pixel >> 16) & 0xff; // red
let g2 = (pixel >> 7) & 0x1fe; // 2 * green
let b = pixel & 0xff; // blue
// Calculate green-favouring average cheaply