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;