mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-28 05:12:34 +00:00
continued progress on aztec, no pass
This commit is contained in:
@@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,7 +34,12 @@
|
|||||||
// import java.util.Random;
|
// import java.util.Random;
|
||||||
// import java.util.TreeSet;
|
// 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
|
* Tests for the Detector
|
||||||
@@ -42,147 +47,198 @@ use crate::common::BitMatrix;
|
|||||||
* @author Frank Yellin
|
* @author Frank Yellin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testErrorInParameterLocatorZeroZero() {
|
fn testErrorInParameterLocatorZeroZero() {
|
||||||
// Layers=1, CodeWords=1. So the parameter info and its Reed-Solomon info
|
// Layers=1, CodeWords=1. So the parameter info and its Reed-Solomon info
|
||||||
// will be completely zero!
|
// will be completely zero!
|
||||||
testErrorInParameterLocator("X");
|
testErrorInParameterLocator("X");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testErrorInParameterLocatorCompact() {
|
fn testErrorInParameterLocatorCompact() {
|
||||||
testErrorInParameterLocator("This is an example Aztec symbol for Wikipedia.");
|
testErrorInParameterLocator("This is an example Aztec symbol for Wikipedia.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testErrorInParameterLocatorNotCompact() {
|
fn testErrorInParameterLocatorNotCompact() {
|
||||||
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz";
|
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz";
|
||||||
testErrorInParameterLocator(&format!("{}{}{}",alphabet , alphabet , alphabet));
|
testErrorInParameterLocator(&format!("{}{}{}", alphabet, alphabet, alphabet));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that we can tolerate errors in the parameter locator bits
|
// Test that we can tolerate errors in the parameter locator bits
|
||||||
fn testErrorInParameterLocator( data:&str) {
|
fn testErrorInParameterLocator(data: &str) {
|
||||||
let aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS);
|
let aztec = encoder::encoder::encode(data, 25, encoder::encoder::DEFAULT_AZTEC_LAYERS)
|
||||||
let random = new Random(aztec.getMatrix().hashCode()); // pseudo-random, but deterministic
|
.expect("encode should create");
|
||||||
|
let random = rand::thread_rng(); //Random(aztec.getMatrix().hashCode()); // pseudo-random, but deterministic
|
||||||
let layers = aztec.getLayers();
|
let layers = aztec.getLayers();
|
||||||
let compact = aztec.isCompact();
|
let compact = aztec.isCompact();
|
||||||
let orientationPoints = getOrientationPoints(aztec);
|
let orientationPoints = getOrientationPoints(&aztec);
|
||||||
for (boolean isMirror : new boolean[] { false, true }) {
|
for isMirror in [false, true] {
|
||||||
for (BitMatrix matrix : getRotations(aztec.getMatrix())) {
|
// for (boolean isMirror : new boolean[] { false, true }) {
|
||||||
// Systematically try every possible 1- and 2-bit error.
|
for matrix in getRotations(aztec.getMatrix()) {
|
||||||
for (int error1 = 0; error1 < orientationPoints.size(); error1++) {
|
// for (BitMatrix matrix : getRotations(aztec.getMatrix())) {
|
||||||
for (int error2 = error1; error2 < orientationPoints.size(); error2++) {
|
// Systematically try every possible 1- and 2-bit error.
|
||||||
BitMatrix copy = isMirror ? transpose(matrix) : clone(matrix);
|
for error1 in 0..orientationPoints.size() {
|
||||||
copy.flip(orientationPoints.get(error1).getX(), orientationPoints.get(error1).getY());
|
// for (int error1 = 0; error1 < orientationPoints.size(); error1++) {
|
||||||
if (error2 > error1) {
|
for error2 in error1..orientationPoints.size() {
|
||||||
// if error2 == error1, we only test a single error
|
// for (int error2 = error1; error2 < orientationPoints.size(); error2++) {
|
||||||
copy.flip(orientationPoints.get(error2).getX(), orientationPoints.get(error2).getY());
|
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(©, 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(©, 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
|
// Zooms a bit matrix so that each bit is factor x factor
|
||||||
fn makeLarger( input:&BitMatrix, factor:u32) -> BitMatrix{
|
fn makeLarger(input: &BitMatrix, factor: u32) -> BitMatrix {
|
||||||
let width = input.getWidth();
|
let width = input.getWidth();
|
||||||
let output = BitMatrix::new(width * factor);
|
let output = BitMatrix::with_single_dimension(width * factor);
|
||||||
for (int inputY = 0; inputY < width; inputY++) {
|
for inputY in 0..width {
|
||||||
for (int inputX = 0; inputX < width; inputX++) {
|
// for (int inputY = 0; inputY < width; inputY++) {
|
||||||
if (input.get(inputX, inputY)) {
|
for inputX in 0..width {
|
||||||
output.setRegion(inputX * factor, inputY * factor, factor, factor);
|
// for (int inputX = 0; inputX < width; inputX++) {
|
||||||
|
if input.get(inputX, inputY) {
|
||||||
|
output.setRegion(inputX * factor, inputY * factor, factor, factor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a list of the four rotations of the BitMatrix.
|
// Returns a list of the four rotations of the BitMatrix.
|
||||||
fn getRotations( matrix0:&BitMatrix)-> Vec<BitMatrix> {
|
fn getRotations(matrix0: &BitMatrix) -> Vec<BitMatrix> {
|
||||||
let matrix90 = rotateRight(matrix0);
|
let matrix90 = rotateRight(matrix0);
|
||||||
let matrix180 = rotateRight(matrix90);
|
let matrix180 = rotateRight(&matrix90);
|
||||||
let matrix270 = rotateRight(matrix180);
|
let matrix270 = rotateRight(&matrix180);
|
||||||
return Arrays.asList(matrix0, matrix90, matrix180, matrix270);
|
vec![*matrix0, matrix90, matrix180, matrix270]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotates a square BitMatrix to the right by 90 degrees
|
// Rotates a square BitMatrix to the right by 90 degrees
|
||||||
fn rotateRight( input:&BitMatrix) -> BitMatrix{
|
fn rotateRight(input: &BitMatrix) -> BitMatrix {
|
||||||
let width = input.getWidth();
|
let width = input.getWidth();
|
||||||
let result = new BitMatrix(width);
|
let result = BitMatrix::with_single_dimension(width);
|
||||||
for (int x = 0; x < width; x++) {
|
for x in 0..width {
|
||||||
for (int y = 0; y < width; y++) {
|
// for (int x = 0; x < width; x++) {
|
||||||
if (input.get(x,y)) {
|
for y in 0..width {
|
||||||
result.set(y, width - x - 1);
|
// for (int y = 0; y < width; y++) {
|
||||||
|
if (input.get(x, y)) {
|
||||||
|
result.set(y, width - x - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the transpose of a bit matrix, which is equivalent to rotating the
|
// Returns the transpose of a bit matrix, which is equivalent to rotating the
|
||||||
// matrix to the right, and then flipping it left-to-right
|
// matrix to the right, and then flipping it left-to-right
|
||||||
fn transpose( input:&BitMatrix) -> BitMatrix {
|
fn transpose(input: &BitMatrix) -> BitMatrix {
|
||||||
let width = input.getWidth();
|
let width = input.getWidth();
|
||||||
let result = new BitMatrix(width);
|
let result = BitMatrix::with_single_dimension(width);
|
||||||
for (int x = 0; x < width; x++) {
|
for x in 0..width {
|
||||||
for (int y = 0; y < width; y++) {
|
// for (int x = 0; x < width; x++) {
|
||||||
if (input.get(x, y)) {
|
for y in 0..width {
|
||||||
result.set(y, x);
|
// for (int y = 0; y < width; y++) {
|
||||||
|
if (input.get(x, y)) {
|
||||||
|
result.set(y, x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone( input:&BitMatrix) -> BitMatrix {
|
fn clone(input: &BitMatrix) -> BitMatrix {
|
||||||
let width = input.getWidth();
|
let width = input.getWidth();
|
||||||
let result = new BitMatrix(width);
|
let result = BitMatrix::with_single_dimension(width);
|
||||||
for (int x = 0; x < width; x++) {
|
for x in 0..width {
|
||||||
for (int y = 0; y < width; y++) {
|
// for (int x = 0; x < width; x++) {
|
||||||
if (input.get(x,y)) {
|
for y in 0..width {
|
||||||
result.set(x,y);
|
// 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 center = code.getMatrix().getWidth() / 2;
|
||||||
let offset = code.isCompact() ? 5 : 7;
|
let offset = if code.isCompact() { 5 } else { 7 };
|
||||||
let result = new ArrayList<>();
|
let result = Vec::new();
|
||||||
for (int xSign = -1; xSign <= 1; xSign += 2) {
|
let mut xSign = -1;
|
||||||
for (int ySign = -1; ySign <= 1; ySign += 2) {
|
while xSign <= 1 {
|
||||||
result.add(new Point(center + xSign * offset, center + ySign * offset));
|
// for (int xSign = -1; xSign <= 1; xSign += 2) {
|
||||||
result.add(new Point(center + xSign * (offset - 1), center + ySign * offset));
|
let mut ySign = -1;
|
||||||
result.add(new Point(center + xSign * offset, center + ySign * (offset - 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,15 @@
|
|||||||
// import java.util.Random;
|
// import java.util.Random;
|
||||||
// import java.util.regex.Pattern;
|
// 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.
|
* Aztec 2D generator unit tests.
|
||||||
@@ -45,11 +53,11 @@ use crate::common::BitArray;
|
|||||||
* @author Frank Yellin
|
* @author Frank Yellin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const ISO_8859_1:&'static encoding::Encoding = StandardCharsets.ISO_8859_1;
|
const ISO_8859_1:EncodingRef = encoding::all::ISO_8859_1;//StandardCharsets.ISO_8859_1;
|
||||||
const UTF_8 :&'static encoding::Encoding= StandardCharsets.UTF_8;
|
const UTF_8 :EncodingRef= encoding::all::UTF_8; //StandardCharsets.UTF_8;
|
||||||
const SHIFT_JIS:&'static encoding::Encoding = Charset.forName("Shift_JIS");
|
const SHIFT_JIS:EncodingRef = encoding::label::encoding_from_whatwg_label("Shift_JIS").expect("must exist");//Charset.forName("Shift_JIS");
|
||||||
const ISO_8859_15:&'static encoding::Encoding = Charset.forName("ISO-8859-15");
|
const ISO_8859_15:EncodingRef = encoding::all::ISO_8859_15;//Charset.forName("ISO-8859-15");
|
||||||
const WINDOWS_1252 :&'static encoding::Encoding= Charset.forName("Windows-1252");
|
const WINDOWS_1252 :EncodingRef= encoding::all::WINDOWS_1252;//Charset.forName("Windows-1252");
|
||||||
|
|
||||||
const DOTX : &str = "[^.X]";
|
const DOTX : &str = "[^.X]";
|
||||||
const SPACES :&str= "\\s+";
|
const SPACES :&str= "\\s+";
|
||||||
@@ -60,98 +68,100 @@ use crate::common::BitArray;
|
|||||||
#[test]
|
#[test]
|
||||||
fn testEncode1() {
|
fn testEncode1() {
|
||||||
testEncode("This is an example Aztec symbol for Wikipedia.", true, 3,
|
testEncode("This is an example Aztec symbol for Wikipedia.", true, 3,
|
||||||
"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 \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 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 \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 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 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 \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 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 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 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 \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 X X X X X X X X \n");
|
X X X X X X X X X X
|
||||||
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testEncode2() {
|
fn testEncode2() {
|
||||||
testEncode("Aztec Code is a public domain 2D matrix barcode symbology" +
|
testEncode(r"Aztec Code is a public domain 2D matrix barcode symbology
|
||||||
" of nominally square symbols built on a square grid with a " +
|
of nominally square symbols built on a square grid with a
|
||||||
"distinctive square bullseye pattern at their center.", false, 6,
|
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" +
|
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 \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 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 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 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 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 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 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 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
|
||||||
" 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 X \n");
|
X X X X X X X X X X X X X
|
||||||
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn testAztecWriter() {
|
fn testAztecWriter() {
|
||||||
testWriter("Espa\u{00F1}ol", null, 25, true, 1); // Without ECI (implicit ISO-8859-1)
|
testWriter("Espa\u{00F1}ol", None, 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("Espa\u{00F1}ol", Some(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.", Some(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.", Some(ISO_8859_15), 25, true, 2);
|
||||||
testWriter("\u{20AC} 1 sample data.", UTF_8, 25, true, 2);
|
testWriter("\u{20AC} 1 sample data.", Some(UTF_8), 25, true, 2);
|
||||||
testWriter("\u{20AC} 1 sample data.", UTF_8, 100, true, 3);
|
testWriter("\u{20AC} 1 sample data.", Some(UTF_8), 100, true, 3);
|
||||||
testWriter("\u{20AC} 1 sample data.", UTF_8, 300, true, 4);
|
testWriter("\u{20AC} 1 sample data.", Some(UTF_8), 300, true, 4);
|
||||||
testWriter("\u{20AC} 1 sample data.", UTF_8, 500, false, 5);
|
testWriter("\u{20AC} 1 sample data.", Some(UTF_8), 500, false, 5);
|
||||||
testWriter("The capital of Japan is named \u{6771}\u{4EAC}.", SHIFT_JIS, 25, true, 3);
|
testWriter("The capital of Japan is named \u{6771}\u{4EAC}.", Some(SHIFT_JIS), 25, true, 3);
|
||||||
// Test AztecWriter defaults
|
// Test AztecWriter defaults
|
||||||
let data = "In ut magna vel mauris malesuada";
|
let data = "In ut magna vel mauris malesuada";
|
||||||
let writer = AztecWriter::new();
|
// let writer = AztecWriter{};
|
||||||
let matrix = writer.encode(data, BarcodeFormat.AZTEC, 0, 0);
|
let matrix = AztecWriter::encode(data, &BarcodeFormat::AZTEC, 0, 0).expect("matrix must exist");
|
||||||
let aztec = Encoder.encode(data,
|
let aztec = encoder::encode(data,
|
||||||
Encoder.DEFAULT_EC_PERCENT, Encoder.DEFAULT_AZTEC_LAYERS);
|
encoder::DEFAULT_EC_PERCENT, encoder::DEFAULT_AZTEC_LAYERS).expect("encode should succeed");
|
||||||
let expectedMatrix = aztec.getMatrix();
|
let expectedMatrix = aztec.getMatrix();
|
||||||
assertEquals(matrix, expectedMatrix);
|
assert_eq!(&matrix, expectedMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
// synthetic tests (encode-decode round-trip)
|
// synthetic tests (encode-decode round-trip)
|
||||||
@@ -178,97 +188,97 @@ use crate::common::BitArray;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testEncodeDecode5() {
|
fn testEncodeDecode5() {
|
||||||
testEncodeDecode("http://test/~!@#*^%&)__ ;:'\"[]{}\\|-+-=`1029384756<>/?abc"
|
testEncodeDecode(r#"http://test/~!@#*^%&)__ ;:'"[]{}\|-+-=`1029384756<>/?abc
|
||||||
+ "Four score and seven our forefathers brought forth", false, 5);
|
Four score and seven our forefathers brought forth"#, false, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testEncodeDecode10() {
|
fn testEncodeDecode10() {
|
||||||
testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" +
|
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" +
|
cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum
|
||||||
" est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" +
|
est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue
|
||||||
" auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" +
|
auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla
|
||||||
" ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" +
|
ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id
|
||||||
" elementum sapien dolor et diam.", false, 10);
|
elementum sapien dolor et diam.", false, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testEncodeDecode23() {
|
fn testEncodeDecode23() {
|
||||||
testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" +
|
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" +
|
cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum
|
||||||
" est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" +
|
est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue
|
||||||
" auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" +
|
auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla
|
||||||
" ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" +
|
ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id
|
||||||
" elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend." +
|
elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend.
|
||||||
" Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus" +
|
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" +
|
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" +
|
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" +
|
quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec
|
||||||
" laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida," +
|
laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida
|
||||||
" justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec" +
|
justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec
|
||||||
" lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar" +
|
lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar
|
||||||
" nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat" +
|
nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat
|
||||||
" eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra" +
|
eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra
|
||||||
" fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo" +
|
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" +
|
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" +
|
ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum
|
||||||
" sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." +
|
sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet.
|
||||||
" Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit" +
|
Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit
|
||||||
" felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo" +
|
felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo
|
||||||
" erat pulvinar nisi, id elementum sapien dolor et diam.", false, 23);
|
erat pulvinar nisi, id elementum sapien dolor et diam.", false, 23);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testEncodeDecode31() {
|
fn testEncodeDecode31() {
|
||||||
testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" +
|
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" +
|
cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum
|
||||||
" est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" +
|
est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue
|
||||||
" auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" +
|
auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla
|
||||||
" ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" +
|
ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id
|
||||||
" elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend." +
|
elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend.
|
||||||
" Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus" +
|
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" +
|
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" +
|
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" +
|
quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec
|
||||||
" laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida," +
|
laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida,
|
||||||
" justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec" +
|
justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec
|
||||||
" lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar" +
|
lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar
|
||||||
" nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat" +
|
nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat
|
||||||
" eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra" +
|
eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra
|
||||||
" fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo" +
|
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" +
|
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" +
|
ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum
|
||||||
" sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." +
|
sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet.
|
||||||
" Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit" +
|
Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit
|
||||||
" felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo" +
|
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" +
|
erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit
|
||||||
" placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at" +
|
placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at
|
||||||
" pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est." +
|
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" +
|
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" +
|
dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id
|
||||||
" justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum" +
|
justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum
|
||||||
" sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat," +
|
sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat,
|
||||||
" eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet" +
|
eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet
|
||||||
" laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac" +
|
laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac
|
||||||
" nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula," +
|
nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula,
|
||||||
" massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus" +
|
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." +
|
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" +
|
Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum
|
||||||
" sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." +
|
sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet.
|
||||||
" Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget" +
|
Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget
|
||||||
" hendrerit felis turpis nec lorem.", false, 31);
|
hendrerit felis turpis nec lorem.", false, 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testGenerateModeMessage() {
|
fn testGenerateModeMessage() {
|
||||||
testModeMessage(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X");
|
testModeMessageComplex(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X");
|
||||||
testModeMessage(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX..");
|
testModeMessageComplex(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");
|
testModeMessageComplex(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(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testStuffBits() {
|
fn testStuffBitsTest() {
|
||||||
testStuffBits(5, ".X.X. X.X.X .X.X.",
|
testStuffBits(5, ".X.X. X.X.X .X.X.",
|
||||||
".X.X. X.X.X .X.X.");
|
".X.X. X.X.X .X.X.");
|
||||||
testStuffBits(5, ".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'
|
//'A' 'B' 'C' B/S =1 'd' 'E' 'F' 'G'
|
||||||
"...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X...");
|
"...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X...");
|
||||||
|
|
||||||
testHighLevelEncodeString(
|
testHighLevelEncodeStringCount(
|
||||||
// Found on an airline boarding pass. Several stretches of Binary shift are
|
// Found on an airline boarding pass. Several stretches of Binary shift are
|
||||||
// necessary to keep the bitcount so low.
|
// necessary to keep the bitcount so low.
|
||||||
"09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEi"
|
"09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEiYgh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
|
||||||
+ "Ygh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
|
|
||||||
823);
|
823);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,62 +354,74 @@ use crate::common::BitArray;
|
|||||||
|
|
||||||
// Create a string in which every character requires binary
|
// Create a string in which every character requires binary
|
||||||
let sb = String::new();
|
let sb = String::new();
|
||||||
for (int i = 0; i <= 3000; i++) {
|
for i in 0..3000 {
|
||||||
sb.append((char) (128 + (i % 30)));
|
// 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
|
// Test the output generated by Binary/Switch, particularly near the
|
||||||
// places where the encoding changes: 31, 62, and 2047+31=2078
|
// 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,
|
for i in [1, 2, 3, 10, 29, 30, 31, 32, 33,
|
||||||
60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100 }) {
|
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"
|
// This is the expected length of a binary string of length "i"
|
||||||
int expectedLength = (8 * i) +
|
let expectedLength = (8 * i) +
|
||||||
((i <= 31) ? 10 : (i <= 62) ? 20 : (i <= 2078) ? 21 : 31);
|
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.
|
// Verify that we are correct about the length.
|
||||||
testHighLevelEncodeString(sb.substring(0, i), expectedLength);
|
testHighLevelEncodeStringCount(&sb[..i], expectedLength as u32);
|
||||||
if (i != 1 && i != 32 && i != 2079) {
|
if i != 1 && i != 32 && i != 2079 {
|
||||||
// The addition of an 'a' at the beginning or end gets merged into the binary code
|
// 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.
|
// 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
|
// So we exclude the border cases i=1,32,2079
|
||||||
// A lower case letter at the beginning will be merged into binary mode
|
// 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
|
// 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.
|
// 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();
|
sb.clear();
|
||||||
for (int i = 0; i < 32; i++) {
|
for i in 0..32 {
|
||||||
sb.append('§'); // § forces binary encoding
|
// 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)
|
// expect B/S(1) A B/S(30)
|
||||||
testHighLevelEncodeString(sb.toString(), 5 + 20 + 31 * 8);
|
testHighLevelEncodeStringCount(&sb, 5 + 20 + 31 * 8);
|
||||||
|
|
||||||
sb = new StringBuilder();
|
sb.clear();
|
||||||
for (int i = 0; i < 31; i++) {
|
for i in 0..31 {
|
||||||
sb.append('§');
|
// 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)
|
// expect B/S(31)
|
||||||
testHighLevelEncodeString(sb.toString(), 10 + 31 * 8);
|
testHighLevelEncodeStringCount(&sb, 10 + 31 * 8);
|
||||||
|
|
||||||
sb = new StringBuilder();
|
sb.clear();
|
||||||
for (int i = 0; i < 34; i++) {
|
for i in 0..34 {
|
||||||
sb.append('§');
|
// 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)
|
// expect B/S(31) B/S(3)
|
||||||
testHighLevelEncodeString(sb.toString(), 20 + 34 * 8);
|
testHighLevelEncodeStringCount(&sb, 20 + 34 * 8);
|
||||||
|
|
||||||
sb = new StringBuilder();
|
sb.clear();
|
||||||
for (int i = 0; i < 64; i++) {
|
for i in 0..64 {
|
||||||
sb.append('§');
|
// 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)
|
// expect B/S(64)
|
||||||
testHighLevelEncodeString(sb.toString(), 21 + 64 * 8);
|
testHighLevelEncodeStringCount(&sb, 21 + 64 * 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -420,7 +441,7 @@ use crate::common::BitArray;
|
|||||||
// 'A' D/L '.' ' ' '1' '2' '3' '4'
|
// 'A' D/L '.' ' ' '1' '2' '3' '4'
|
||||||
"...X. XXXX. XX.X ...X ..XX .X.. .X.X .X X.");
|
"...X. XXXX. XX.X ...X ..XX .X.. .X.X .X X.");
|
||||||
// Don't bother leaving Binary Shift.
|
// Don't bother leaving Binary Shift.
|
||||||
testHighLevelEncodeString("A\200. \200",
|
testHighLevelEncodeString("A\u{200}. \u{200}",
|
||||||
// 'A' B/S =2 \200 "." " " \200
|
// 'A' B/S =2 \200 "." " " \200
|
||||||
"...X. XXXXX ..X.. X....... ..X.XXX. ..X..... X.......");
|
"...X. XXXXX ..X.. X....... ..X.XXX. ..X..... X.......");
|
||||||
}
|
}
|
||||||
@@ -434,20 +455,20 @@ use crate::common::BitArray;
|
|||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn testUserSpecifiedLayers2() {
|
fn testUserSpecifiedLayers2() {
|
||||||
doTestUserSpecifiedLayers(-1);
|
doTestUserSpecifiedLayers(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doTestUserSpecifiedLayers( userSpecifiedLayers:usize) {
|
fn doTestUserSpecifiedLayers( userSpecifiedLayers:usize) {
|
||||||
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
AztecCode aztec = Encoder.encode(alphabet, 25, -2);
|
let aztec = encoder::encode(alphabet, 25, 2).expect("should encode");
|
||||||
assertEquals(2, aztec.getLayers());
|
assert_eq!(2, aztec.getLayers());
|
||||||
assertTrue(aztec.isCompact());
|
assert!(aztec.isCompact());
|
||||||
|
|
||||||
aztec = Encoder.encode(alphabet, 25, 32);
|
aztec = encoder::encode(alphabet, 25, 32).expect("should encode");
|
||||||
assertEquals(32, aztec.getLayers());
|
assert_eq!(32, aztec.getLayers());
|
||||||
assertFalse(aztec.isCompact());
|
assert!(!aztec.isCompact());
|
||||||
|
|
||||||
Encoder.encode(alphabet, 25, userSpecifiedLayers);
|
encoder::encode(alphabet, 25, userSpecifiedLayers as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -455,132 +476,139 @@ use crate::common::BitArray;
|
|||||||
fn testBorderCompact4CaseFailed() {
|
fn testBorderCompact4CaseFailed() {
|
||||||
// Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must
|
// Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must
|
||||||
// be error correction
|
// be error correction
|
||||||
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
// encodes as 26 * 5 * 4 = 520 bits of data
|
// encodes as 26 * 5 * 4 = 520 bits of data
|
||||||
String alphabet4 = alphabet + alphabet + alphabet + alphabet;
|
let alphabet4 = format!("{}{}{}{}",alphabet , alphabet, alphabet , alphabet);
|
||||||
Encoder.encode(alphabet4, 0, -4);
|
encoder::encode(&alphabet4, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn testBorderCompact4Case() {
|
fn testBorderCompact4Case() {
|
||||||
// Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must
|
// Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must
|
||||||
// be error correction
|
// be error correction
|
||||||
String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
// encodes as 26 * 5 * 4 = 520 bits of data
|
// 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
|
// 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);
|
let aztecCode = encoder::encode(&alphabet4, 0, encoder::DEFAULT_AZTEC_LAYERS).expect("Should encode");
|
||||||
assertFalse(aztecCode.isCompact());
|
assert!(!aztecCode.isCompact());
|
||||||
assertEquals(4, aztecCode.getLayers());
|
assert_eq!(4, aztecCode.getLayers());
|
||||||
|
|
||||||
// But shortening the string to 100 bytes (500 bits of data), compact works fine, even if we
|
// But shortening the string to 100 bytes (500 bits of data), compact works fine, even if we
|
||||||
// include more error checking.
|
// include more error checking.
|
||||||
aztecCode = Encoder.encode(alphabet4.substring(0, 100), 10, Encoder.DEFAULT_AZTEC_LAYERS);
|
aztecCode = encoder::encode(&alphabet4[..100], 10, encoder::DEFAULT_AZTEC_LAYERS).expect("should encode");
|
||||||
assertTrue(aztecCode.isCompact());
|
assert!(aztecCode.isCompact());
|
||||||
assertEquals(4, aztecCode.getLayers());
|
assert_eq!(4, aztecCode.getLayers());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper routines
|
// Helper routines
|
||||||
|
|
||||||
fn testEncode( data:&str, compact:bool, layers:usize, expected:&str) {
|
fn testEncode( data:&str, compact:bool, layers:u32, expected:&str) {
|
||||||
AztecCode aztec = Encoder.encode(data, 33, Encoder.DEFAULT_AZTEC_LAYERS);
|
let aztec = encoder::encode(data, 33, encoder::DEFAULT_AZTEC_LAYERS).expect("should encode");
|
||||||
assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact());
|
assert_eq!( compact, aztec.isCompact(),"Unexpected symbol format (compact)");
|
||||||
assertEquals("Unexpected nr. of layers", layers, aztec.getLayers());
|
assert_eq!( layers, aztec.getLayers(),"Unexpected nr. of layers");
|
||||||
BitMatrix matrix = aztec.getMatrix();
|
let matrix = aztec.getMatrix();
|
||||||
assertEquals("encode() failed", expected, matrix.toString());
|
assert_eq!( expected, matrix.to_string(),"encode() failed");
|
||||||
|
let z: BitMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testEncodeDecode( data:&str, compact:bool, layers:usize) {
|
fn testEncodeDecode( data:&str, compact:bool, layers:u32) {
|
||||||
AztecCode aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS);
|
let aztec = encoder::encode(data, 25, encoder::DEFAULT_AZTEC_LAYERS).expect("should encode");
|
||||||
assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact());
|
assert_eq!( compact, aztec.isCompact(),"Unexpected symbol format (compact)");
|
||||||
assertEquals("Unexpected nr. of layers", layers, aztec.getLayers());
|
assert_eq!( layers, aztec.getLayers(),"Unexpected nr. of layers");
|
||||||
BitMatrix matrix = aztec.getMatrix();
|
let mut matrix = aztec.getMatrix();
|
||||||
AztecDetectorRXingResult r =
|
let r =
|
||||||
new AztecDetectorRXingResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
|
AztecDetectorRXingResult::new(matrix.clone(), NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
|
||||||
DecoderRXingResult res = new Decoder().decode(r);
|
let res = decoder::decode(&r).expect("decode ok");
|
||||||
assertEquals(data, res.getText());
|
assert_eq!(data, res.getText());
|
||||||
// Check error correction by introducing a few minor errors
|
// 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()), random.nextInt(2));
|
||||||
matrix.flip(random.nextInt(matrix.getWidth()), matrix.getHeight() - 2 + 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(random.nextInt(2), random.nextInt(matrix.getHeight()));
|
||||||
matrix.flip(matrix.getWidth() - 2 + 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());
|
r = AztecDetectorRXingResult::new(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
|
||||||
res = new Decoder().decode(r);
|
res = decoder::decode(r);
|
||||||
assertEquals(data, res.getText());
|
assert_eq!(data, res.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testWriter( data:&str,
|
fn testWriter( data:&str,
|
||||||
charset:&'static dyn encoding::Encoding,
|
charset:Option<EncodingRef>,
|
||||||
eccPercent:u32,
|
eccPercent:u32,
|
||||||
compact:bool,
|
compact:bool,
|
||||||
layers:usize) throws FormatException {
|
layers:u32) {
|
||||||
// Perform an encode-decode round-trip because it can be lossy.
|
// Perform an encode-decode round-trip because it can be lossy.
|
||||||
Map<EncodeHintType,Object> hints = new EnumMap<>(EncodeHintType.class);
|
let hints = HashMap::new();
|
||||||
if (null != charset) {
|
if charset.is_some() {
|
||||||
hints.put(EncodeHintType.CHARACTER_SET, charset.name());
|
hints.insert(EncodingHintType::CHARACTER_SET, EncodingHintValue::CharacterSet(charset.unwrap().name()));
|
||||||
}
|
}
|
||||||
hints.put(EncodeHintType.ERROR_CORRECTION, eccPercent);
|
// if (null != charset) {
|
||||||
AztecWriter writer = new AztecWriter();
|
// hints.put(EncodeHintType.CHARACTER_SET, charset.name());
|
||||||
BitMatrix matrix = writer.encode(data, BarcodeFormat.AZTEC, 0, 0, hints);
|
// }
|
||||||
AztecCode aztec = Encoder.encode(data, eccPercent,
|
hints.insert(EncodeHintType::ERROR_CORRECTION, eccPercent);
|
||||||
Encoder.DEFAULT_AZTEC_LAYERS, charset);
|
let writer = AztecWriter{};
|
||||||
assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact());
|
let matrix = AztecWriter::encode_with_hints(data, &BarcodeFormat::AZTEC, 0, 0, &hints).expect("encoder created");
|
||||||
assertEquals("Unexpected nr. of layers", layers, aztec.getLayers());
|
let aztec = encoder::encode_with_charset(data, eccPercent,
|
||||||
BitMatrix matrix2 = aztec.getMatrix();
|
encoder::DEFAULT_AZTEC_LAYERS, charset.unwrap()).expect("encode should encode");
|
||||||
assertEquals(matrix, matrix2);
|
assert_eq!( compact, aztec.isCompact(),"Unexpected symbol format (compact)");
|
||||||
AztecDetectorRXingResult r =
|
assert_eq!( layers, aztec.getLayers(),"Unexpected nr. of layers");
|
||||||
new AztecDetectorRXingResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
|
let matrix2 = aztec.getMatrix();
|
||||||
DecoderRXingResult res = new Decoder().decode(r);
|
assert_eq!(&matrix, matrix2);
|
||||||
assertEquals(data, res.getText());
|
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
|
// Check error correction by introducing up to eccPercent/2 errors
|
||||||
int ecWords = aztec.getCodeWords() * eccPercent / 100 / 2;
|
let ecWords = aztec.getCodeWords() * eccPercent / 100 / 2;
|
||||||
Random random = getPseudoRandom();
|
let random = getPseudoRandom();
|
||||||
for (int i = 0; i < ecWords; i++) {
|
for i in 0 ..ecWords {
|
||||||
|
// for (int i = 0; i < ecWords; i++) {
|
||||||
// don't touch the core
|
// don't touch the core
|
||||||
int x = random.nextBoolean() ?
|
let x = if random.nextBoolean()
|
||||||
random.nextInt(aztec.getLayers() * 2)
|
{random.nextInt(aztec.getLayers() * 2)}
|
||||||
: matrix.getWidth() - 1 - random.nextInt(aztec.getLayers() * 2);
|
else {matrix.getWidth() - 1 - random.nextInt(aztec.getLayers() * 2)};
|
||||||
int y = random.nextBoolean() ?
|
let y = if random.nextBoolean()
|
||||||
random.nextInt(aztec.getLayers() * 2)
|
{random.nextInt(aztec.getLayers() * 2)}
|
||||||
: matrix.getHeight() - 1 - random.nextInt(aztec.getLayers() * 2);
|
else {matrix.getHeight() - 1 - random.nextInt(aztec.getLayers() * 2)};
|
||||||
matrix.flip(x, y);
|
matrix.flip(x, y);
|
||||||
}
|
}
|
||||||
r = new AztecDetectorRXingResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
|
r = AztecDetectorRXingResult::nwq(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers());
|
||||||
res = new Decoder().decode(r);
|
res = decoder::decode(r);
|
||||||
assertEquals(data, res.getText());
|
assert_eq!(data, res.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getPseudoRandom() -> rand::Rng{
|
fn getPseudoRandom() -> rand::rngs::ThreadRng{
|
||||||
return new Random(0xDEADBEEF);
|
rand::thread_rng()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testModeMessage( compact:bool, layers:usize, words:usize, expected:&str) {
|
fn testModeMessageComplex( compact:bool, layers:u32, words:usize, expected:&str) {
|
||||||
BitArray in = Encoder.generateModeMessage(compact, layers, words);
|
let indata = encoder::generateModeMessage(compact, layers, words);
|
||||||
assertEquals("generateModeMessage() failed", stripSpace(expected), stripSpace(in.toString()));
|
assert_eq!( stripSpace(expected), stripSpace(indata.toString()),"generateModeMessage() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testStuffBits( wordSize:usize, bits:&str, expected:&str) {
|
fn testStuffBits( wordSize:usize, bits:&str, expected:&str) {
|
||||||
BitArray in = toBitArray(bits);
|
let indata = toBitArray(bits);
|
||||||
BitArray stuffed = Encoder.stuffBits(in, wordSize);
|
let stuffed = encoder::stuffBits(&indata, wordSize);
|
||||||
assertEquals("stuffBits() failed for input string: " + bits,
|
assert_eq!(
|
||||||
stripSpace(expected), stripSpace(stuffed.toString()));
|
stripSpace(expected), stripSpace(stuffed.toString()),
|
||||||
|
"stuffBits() failed for input string: {}" , bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn testHighLevelEncodeString( s:&str, expectedBits:&str) {
|
fn testHighLevelEncodeString( s:&str, expectedBits:&str) {
|
||||||
BitArray bits = new HighLevelEncoder(s.getBytes(StandardCharsets.ISO_8859_1)).encode();
|
let bits = HighLevelEncoder::new(s.getBytes(StandardCharsets.ISO_8859_1)).encode();
|
||||||
String receivedBits = stripSpace(bits.toString());
|
let receivedBits = stripSpace(bits.toString());
|
||||||
assertEquals("highLevelEncode() failed for input string: " + s, stripSpace(expectedBits), receivedBits);
|
assert_eq!( stripSpace(expectedBits), receivedBits,"highLevelEncode() failed for input string: {}" , s);
|
||||||
assertEquals(s, Decoder.highLevelDecode(toBooleanArray(bits)));
|
assert_eq!(s, decoder::highLevelDecode(&toBooleanArray(&bits)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testHighLevelEncodeString( s:&str, expectedReceivedBits:u32) {
|
fn testHighLevelEncodeStringCount( s:&str, expectedReceivedBits:u32) {
|
||||||
BitArray bits = new HighLevelEncoder(s.getBytes(StandardCharsets.ISO_8859_1)).encode();
|
let bits = HighLevelEncoder::new(s.getBytes(StandardCharsets.ISO_8859_1)).encode().unwrap();
|
||||||
int receivedBitCount = stripSpace(bits.toString()).length();
|
let receivedBitCount = stripSpace(bits.toString()).len();
|
||||||
assertEquals("highLevelEncode() failed for input string: " + s,
|
assert_eq!(
|
||||||
expectedReceivedBits, receivedBitCount);
|
expectedReceivedBits, receivedBitCount,
|
||||||
assertEquals(s, Decoder.highLevelDecode(toBooleanArray(bits)));
|
"highLevelEncode() failed for input string: {}" , s);
|
||||||
|
assert_eq!(s, decoder::highLevelDecode(&toBooleanArray(&bits)));
|
||||||
}
|
}
|
||||||
|
|||||||
123
src/aztec/aztec_reader.rs
Normal file
123
src/aztec/aztec_reader.rs
Normal 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
157
src/aztec/aztec_writer.rs
Normal 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)
|
||||||
|
}
|
||||||
@@ -735,7 +735,7 @@ impl Detector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
struct Point {
|
pub struct Point {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,6 +30,16 @@ pub struct AztecCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
* @return {@code true} if compact instead of full mode
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use std::fmt;
|
|||||||
|
|
||||||
use crate::common::BitArray;
|
use crate::common::BitArray;
|
||||||
|
|
||||||
#[derive(Debug,PartialEq, Eq,Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct BinaryShiftToken {
|
pub struct BinaryShiftToken {
|
||||||
binaryShiftStart: u32,
|
binaryShiftStart: u32,
|
||||||
binaryShiftByteCount: u32,
|
binaryShiftByteCount: u32,
|
||||||
@@ -44,7 +44,7 @@ impl BinaryShiftToken {
|
|||||||
bitArray.appendBits(bsbc as u32 - 31, 16);
|
bitArray.appendBits(bsbc as u32 - 31, 16);
|
||||||
} else if (i == 0) {
|
} else if (i == 0) {
|
||||||
// 1 <= binaryShiftByteCode <= 62
|
// 1 <= binaryShiftByteCode <= 62
|
||||||
bitArray.appendBits(bsbc.min( 31) as u32, 5);
|
bitArray.appendBits(bsbc.min(31) as u32, 5);
|
||||||
} else {
|
} else {
|
||||||
// 32 <= binaryShiftCount <= 62 and i == 31
|
// 32 <= binaryShiftCount <= 62 and i == 31
|
||||||
bitArray.appendBits(bsbc as u32 - 31, 5);
|
bitArray.appendBits(bsbc as u32 - 31, 5);
|
||||||
|
|||||||
523
src/aztec/encoder/encoder.rs
Normal file
523
src/aztec/encoder/encoder.rs
Normal 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;
|
||||||
|
}
|
||||||
@@ -14,9 +14,14 @@
|
|||||||
* limitations under the License.
|
* 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
|
* This produces nearly optimal encodings of text into the first-level of
|
||||||
@@ -31,342 +36,425 @@ use crate::common::BitArray;
|
|||||||
* @author Rustam Abdullaev
|
* @author Rustam Abdullaev
|
||||||
*/
|
*/
|
||||||
pub struct HighLevelEncoder {
|
pub struct HighLevelEncoder {
|
||||||
|
text: Vec<u8>,
|
||||||
text:Vec<u8>,
|
charset: &'static dyn encoding::Encoding,
|
||||||
charset: &'static dyn encoding::Encoding,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HighLevelEncoder {
|
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
|
pub const MODE_UPPER: usize = 0; // 5 bits
|
||||||
const MODE_LOWER :u32 = 1; // 5 bits
|
pub const MODE_LOWER: usize = 1; // 5 bits
|
||||||
const MODE_DIGIT :u32 = 2; // 4 bits
|
pub const MODE_DIGIT: usize = 2; // 4 bits
|
||||||
const MODE_MIXED :u32 = 3; // 5 bits
|
pub const MODE_MIXED: usize = 3; // 5 bits
|
||||||
const MODE_PUNCT :u32 = 4; // 5 bits
|
pub const MODE_PUNCT: usize = 4; // 5 bits
|
||||||
|
|
||||||
// The Latch Table shows, for each pair of Modes, the optimal method for
|
// 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
|
// 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!
|
// 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 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
|
// The low half-word of each entry are the actual bits necessary to change
|
||||||
const LATCH_TABLE : [[u32]]= [
|
pub const LATCH_TABLE: [[u32; 5]; 5] = [
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
(5 << 16) + 28, // UPPER -> LOWER
|
(5 << 16) + 28, // UPPER -> LOWER
|
||||||
(5 << 16) + 30, // UPPER -> DIGIT
|
(5 << 16) + 30, // UPPER -> DIGIT
|
||||||
(5 << 16) + 29, // UPPER -> MIXED
|
(5 << 16) + 29, // UPPER -> MIXED
|
||||||
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
|
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
|
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
|
||||||
0,
|
0,
|
||||||
(5 << 16) + 30, // LOWER -> DIGIT
|
(5 << 16) + 30, // LOWER -> DIGIT
|
||||||
(5 << 16) + 29, // LOWER -> MIXED
|
(5 << 16) + 29, // LOWER -> MIXED
|
||||||
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
|
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
(4 << 16) + 14, // DIGIT -> UPPER
|
(4 << 16) + 14, // DIGIT -> UPPER
|
||||||
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
|
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
|
||||||
0,
|
0,
|
||||||
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
|
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
|
||||||
(14 << 16) + (14 << 10) + (29 << 5) + 30,
|
(14 << 16) + (14 << 10) + (29 << 5) + 30,
|
||||||
] // DIGIT -> UPPER -> MIXED -> PUNCT
|
], // DIGIT -> UPPER -> MIXED -> PUNCT
|
||||||
,
|
[
|
||||||
[
|
(5 << 16) + 29, // MIXED -> UPPER
|
||||||
(5 << 16) + 29, // MIXED -> UPPER
|
(5 << 16) + 28, // MIXED -> LOWER
|
||||||
(5 << 16) + 28, // MIXED -> LOWER
|
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
|
||||||
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
|
0,
|
||||||
0,
|
(5 << 16) + 30, // MIXED -> PUNCT
|
||||||
(5 << 16) + 30, // MIXED -> PUNCT
|
],
|
||||||
],
|
[
|
||||||
[
|
(5 << 16) + 31, // PUNCT -> UPPER
|
||||||
(5 << 16) + 31, // PUNCT -> UPPER
|
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
|
||||||
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
|
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
|
||||||
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
|
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
|
||||||
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
|
0,
|
||||||
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'
|
|
||||||
];
|
];
|
||||||
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
|
// A reverse mapping from [mode][char] to the encoding for that character
|
||||||
// shown
|
// in that mode. An entry of 0 indicates no mapping exists.
|
||||||
const SHIFT_TABLE : [[i32]]= { // mode shift codes, per table
|
pub const CHAR_MAP: [[u8; 256]; 5] = {
|
||||||
let mut shift_table = [[-1i32;6];6];
|
let mut char_map = [[0u8; 256]; 5];
|
||||||
|
char_map[Self::MODE_UPPER][b' ' as usize] = 1;
|
||||||
shift_table[Self::MODE_UPPER][Self::MODE_PUNCT] = 0;
|
let mut c = b'A';
|
||||||
|
while c <= b'Z' {
|
||||||
shift_table[Self::MODE_LOWER][Self::MODE_PUNCT] = 0;
|
char_map[Self::MODE_UPPER][c as usize] = c - b'A' + 2;
|
||||||
shift_table[Self::MODE_LOWER][Self::MODE_UPPER] = 28;
|
c += 1;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
// Try generating the character by latching to its mode
|
// for (int c = 'A'; c <= 'Z'; c++) {
|
||||||
if (!charInCurrentTable || mode == state.getMode() || mode == MODE_DIGIT) {
|
// char_map[Self::MODE_UPPER][c] = c - 'A' + 2;
|
||||||
// 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
|
char_map[Self::MODE_LOWER][b' ' as usize] = 1;
|
||||||
// other latch would be equally successful *after* this character, and
|
let mut c = b'a';
|
||||||
// so wouldn't save any bits.
|
while c <= b'z' {
|
||||||
State latchState = stateNoBinary.latchAndAppend(mode, charInMode);
|
char_map[Self::MODE_LOWER][c as usize] = c - b'a' + 2;
|
||||||
result.add(latchState);
|
c += 1;
|
||||||
}
|
}
|
||||||
// Try generating the character by switching to its mode.
|
// for (int c = 'a'; c <= 'z'; c++) {
|
||||||
if (!charInCurrentTable && SHIFT_TABLE[state.getMode()][mode] >= 0) {
|
// char_map[Self::MODE_LOWER][c] = c - 'a' + 2;
|
||||||
// It never makes sense to temporarily shift to another mode if the
|
// }
|
||||||
// character exists in the current mode. That can never save bits.
|
char_map[Self::MODE_DIGIT][b' ' as usize] = 1;
|
||||||
State shiftState = stateNoBinary.shiftAndAppend(mode, charInMode);
|
let mut c = b'0';
|
||||||
result.add(shiftState);
|
while c <= b'9' {
|
||||||
|
char_map[Self::MODE_DIGIT][c as usize] = c - b'0' + 2;
|
||||||
|
c += 1;
|
||||||
}
|
}
|
||||||
}
|
// for (int c = '0'; c <= '9'; c++) {
|
||||||
}
|
// char_map[Self::MODE_DIGIT][c] = c - '0' + 2;
|
||||||
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
|
char_map[Self::MODE_DIGIT][b',' as usize] = 12;
|
||||||
// in binary shift mode, and the character exists in your current mode.
|
char_map[Self::MODE_DIGIT][b'.' as usize] = 13;
|
||||||
// That can never save bits over just outputting the char in the current mode.
|
let mixedTable = [
|
||||||
State binaryState = state.addBinaryShiftChar(index);
|
'\0', ' ', '\u{1}', '\u{2}', '\u{3}', '\u{4}', '\u{5}', '\u{6}', '\u{7}', '\u{8}',
|
||||||
result.add(binaryState);
|
'\t', '\n', '\u{13}', '\u{f}', '\r', '\u{33}', '\u{34}', '\u{35}', '\u{36}', '\u{37}',
|
||||||
}
|
'@', '\\', '^', '_', '`', '|', '~', '\u{177}',
|
||||||
}
|
];
|
||||||
|
let mut i = 0;
|
||||||
private static Collection<State> updateStateListForPair(Iterable<State> states, int index, int pairCode) {
|
while i < mixedTable.len() {
|
||||||
Collection<State> result = new LinkedList<>();
|
char_map[Self::MODE_MIXED][mixedTable[i] as u8 as usize] = i as u8;
|
||||||
for (State state : states) {
|
i += 1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
if (newState.isBetterThanOrEqualTo(oldState)) {
|
// for (int i = 0; i < mixedTable.length; i++) {
|
||||||
iterator.remove();
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
// for (int i = 0; i < punctTable.length; i++) {
|
||||||
if (add) {
|
// if (punctTable[i] > 0) {
|
||||||
result.addFirst(newState);
|
// CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ mod binary_shift_token;
|
|||||||
mod state;
|
mod state;
|
||||||
mod high_level_encoder;
|
mod high_level_encoder;
|
||||||
|
|
||||||
|
pub mod encoder;
|
||||||
|
|
||||||
pub use aztec_code::*;
|
pub use aztec_code::*;
|
||||||
pub use token::*;
|
pub use token::*;
|
||||||
pub use simple_token::*;
|
pub use simple_token::*;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use std::fmt;
|
|||||||
|
|
||||||
use crate::common::BitArray;
|
use crate::common::BitArray;
|
||||||
|
|
||||||
#[derive(Debug,PartialEq, Eq,Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct SimpleToken {
|
pub struct SimpleToken {
|
||||||
// For normal words, indicates value and bitCount
|
// For normal words, indicates value and bitCount
|
||||||
value: u16,
|
value: u16,
|
||||||
@@ -34,7 +34,9 @@ impl SimpleToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn appendTo(&self, bitArray: &mut BitArray, text: &[u8]) {
|
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
|
// @Override
|
||||||
|
|||||||
@@ -16,197 +16,241 @@
|
|||||||
|
|
||||||
use std::fmt;
|
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.
|
* State represents all information about a sequence necessary to generate the current output.
|
||||||
* Note that a state is immutable.
|
* Note that a state is immutable.
|
||||||
*/
|
*/
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct State {
|
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.
|
||||||
// The current mode of the encoding (or the mode to which we'll return if
|
mode: u32,
|
||||||
// we're in Binary Shift mode.
|
// The list of tokens that we output. If we are in Binary Shift mode, this
|
||||||
mode:u32,
|
// token list does *not* yet included the token for those bytes
|
||||||
// The list of tokens that we output. If we are in Binary Shift mode, this
|
token: Token,
|
||||||
// token list does *not* yet included the token for those bytes
|
// If non-zero, the number of most recent bytes that should be output
|
||||||
token:Token,
|
// in Binary Shift mode.
|
||||||
// If non-zero, the number of most recent bytes that should be output
|
binaryShiftByteCount: u32,
|
||||||
// in Binary Shift mode.
|
// The total number of bits generated (including Binary Shift).
|
||||||
binaryShiftByteCount:u32,
|
bitCount: u32,
|
||||||
// The total number of bits generated (including Binary Shift).
|
binaryShiftCost: u32,
|
||||||
bitCount:u32,
|
|
||||||
binaryShiftCost:u32,
|
|
||||||
}
|
}
|
||||||
impl State {
|
impl State {
|
||||||
pub fn new( token:Token, mode:u32, binaryBytes:u32, bitCount:u32) -> Self{
|
pub fn new(token: Token, mode: u32, binaryBytes: u32, bitCount: u32) -> Self {
|
||||||
Self{
|
Self {
|
||||||
mode,
|
mode,
|
||||||
token,
|
token,
|
||||||
binaryShiftByteCount: binaryBytes,
|
binaryShiftByteCount: binaryBytes,
|
||||||
bitCount,
|
bitCount,
|
||||||
binaryShiftCost: Self::calculateBinaryShiftCost(binaryBytes),
|
binaryShiftCost: Self::calculateBinaryShiftCost(binaryBytes),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getMode(&self) -> u32{
|
pub fn getMode(&self) -> u32 {
|
||||||
self.mode
|
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;
|
|
||||||
}
|
}
|
||||||
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
|
pub fn getToken(&self) -> &Token {
|
||||||
// necessary different) mode, and then a code.
|
&self.token
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
let latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
|
|
||||||
token.add(value, latchModeBitCount);
|
|
||||||
|
|
||||||
State::new(token, mode, 0, bitCount + latchModeBitCount)
|
pub fn getBinaryShiftByteCount(&self) -> u32 {
|
||||||
}
|
self.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 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
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
|
pub fn getBitCount(&self) -> u32 {
|
||||||
// Binary Shift mode.
|
self.bitCount
|
||||||
pub fn endBinaryShift(self, index:u32) -> State{
|
|
||||||
if self.binaryShiftByteCount == 0 {
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
let token = self.token;
|
|
||||||
self.token.addBinaryShift(index - self.binaryShiftByteCount, self.binaryShiftByteCount);
|
|
||||||
|
|
||||||
State::new(token, self.mode, 0, self.bitCount)
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true if "this" state is better (or equal) to be in than "that"
|
// Create a new state representing this state with a latch to a (not
|
||||||
// state under all possible circumstances.
|
// necessary different) mode, and then a code.
|
||||||
pub fn isBetterThanOrEqualTo(&self, other:&State)->bool {
|
pub fn latchAndAppend(self, mode: u32, value: u32) -> State {
|
||||||
let newModeBitCount = self.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16);
|
let mut bitCount = self.bitCount;
|
||||||
if self.binaryShiftByteCount < other.binaryShiftByteCount {
|
let mut token = self.token;
|
||||||
// add additional B/S encoding cost of other, if any
|
if mode != self.mode {
|
||||||
newModeBitCount += other.binaryShiftCost - self.binaryShiftCost;
|
let latch = HighLevelEncoder::LATCH_TABLE[self.mode as usize][mode as usize];
|
||||||
} else if self.binaryShiftByteCount > other.binaryShiftByteCount && other.binaryShiftByteCount > 0 {
|
token.add(latch as i32 & 0xFFFF, latch >> 16);
|
||||||
// maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it)
|
bitCount += latch >> 16;
|
||||||
newModeBitCount += 10;
|
}
|
||||||
}
|
let latchModeBitCount = if mode == HighLevelEncoder::MODE_DIGIT as u32 {
|
||||||
newModeBitCount <= other.bitCount
|
4
|
||||||
}
|
} else {
|
||||||
|
5
|
||||||
|
};
|
||||||
|
token.add(value as i32, latchModeBitCount);
|
||||||
|
|
||||||
pub fn toBitArray(&self, text:&[u8]) -> BitArray{
|
State::new(token, mode, 0, bitCount + latchModeBitCount)
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
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
|
// Create a new state representing this state, with a temporary shift
|
||||||
// public String toString() {
|
// to a different mode to output a single value.
|
||||||
// return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);
|
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{
|
// Create a new state representing this state, but an additional character
|
||||||
if binaryShiftByteCount > 62 {
|
// output in Binary Shift mode.
|
||||||
return 21; // B/S with extended length
|
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 {
|
impl fmt::Display for State {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,39 +18,83 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use crate::common::BitArray;
|
use crate::common::BitArray;
|
||||||
|
|
||||||
use super::{SimpleToken, BinaryShiftToken};
|
use super::{BinaryShiftToken, SimpleToken};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum TokenType {
|
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 {
|
pub struct Token {
|
||||||
tokens: Vec<TokenType>,
|
tokens: Vec<TokenType>,
|
||||||
current_pointer: usize,
|
//current_pointer: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn new () -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
tokens: vec![TokenType::Empty],
|
tokens: Vec::new(),
|
||||||
current_pointer: 0,
|
//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{
|
// pub enum Token{
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
mod AztecDetectorResult;
|
mod AztecDetectorResult;
|
||||||
|
mod aztec_reader;
|
||||||
|
mod aztec_writer;
|
||||||
pub mod decoder;
|
pub mod decoder;
|
||||||
pub mod detector;
|
pub mod detector;
|
||||||
pub mod encoder;
|
pub mod encoder;
|
||||||
|
|
||||||
|
pub use aztec_reader::*;
|
||||||
|
pub use aztec_writer::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod DecoderTest;
|
mod DecoderTest;
|
||||||
// #[cfg(test)]
|
#[cfg(test)]
|
||||||
// mod EncoderTest;
|
mod EncoderTest;
|
||||||
// #[cfg(test)]
|
#[cfg(test)]
|
||||||
// mod DetectorTest;
|
mod DetectorTest;
|
||||||
|
|
||||||
mod shared_test_methods;
|
mod shared_test_methods;
|
||||||
@@ -770,7 +770,7 @@ pub trait DetectorRXingResult {
|
|||||||
pub struct BitMatrix {
|
pub struct BitMatrix {
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
rowSize: usize,
|
row_size: usize,
|
||||||
bits: Vec<u32>,
|
bits: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -799,7 +799,7 @@ impl BitMatrix {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
rowSize: ((width + 31) / 32) as usize,
|
row_size: ((width + 31) / 32) as usize,
|
||||||
bits: vec![0; (((width + 31) / 32) * height) as usize],
|
bits: vec![0; (((width + 31) / 32) * height) as usize],
|
||||||
})
|
})
|
||||||
// this.width = width;
|
// this.width = width;
|
||||||
@@ -812,7 +812,7 @@ impl BitMatrix {
|
|||||||
Self {
|
Self {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
rowSize,
|
row_size: rowSize,
|
||||||
bits,
|
bits,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -926,7 +926,7 @@ impl BitMatrix {
|
|||||||
* @return value of given bit in matrix
|
* @return value of given bit in matrix
|
||||||
*/
|
*/
|
||||||
pub fn get(&self, x: u32, y: u32) -> bool {
|
pub fn get(&self, x: u32, y: u32) -> bool {
|
||||||
let offset = y as usize * self.rowSize + (x as usize / 32);
|
let offset = y as usize * self.row_size + (x as usize / 32);
|
||||||
return ((self.bits[offset] >> (x & 0x1f)) & 1) != 0;
|
return ((self.bits[offset] >> (x & 0x1f)) & 1) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -937,12 +937,12 @@ impl BitMatrix {
|
|||||||
* @param y The vertical component (i.e. which row)
|
* @param y The vertical component (i.e. which row)
|
||||||
*/
|
*/
|
||||||
pub fn set(&mut self, x: u32, y: u32) {
|
pub fn set(&mut self, x: u32, y: u32) {
|
||||||
let offset = y as usize * self.rowSize + (x as usize / 32);
|
let offset = y as usize * self.row_size + (x as usize / 32);
|
||||||
self.bits[offset] |= 1 << (x & 0x1f);
|
self.bits[offset] |= 1 << (x & 0x1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unset(&mut self, x: u32, y: u32) {
|
pub fn unset(&mut self, x: u32, y: u32) {
|
||||||
let offset = y as usize * self.rowSize + (x as usize / 32);
|
let offset = y as usize * self.row_size + (x as usize / 32);
|
||||||
self.bits[offset] &= !(1 << (x & 0x1f));
|
self.bits[offset] &= !(1 << (x & 0x1f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -953,7 +953,7 @@ impl BitMatrix {
|
|||||||
* @param y The vertical component (i.e. which row)
|
* @param y The vertical component (i.e. which row)
|
||||||
*/
|
*/
|
||||||
pub fn flip_coords(&mut self, x: u32, y: u32) {
|
pub fn flip_coords(&mut self, x: u32, y: u32) {
|
||||||
let offset = y as usize * self.rowSize + (x as usize / 32);
|
let offset = y as usize * self.row_size + (x as usize / 32);
|
||||||
self.bits[offset] ^= 1 << (x & 0x1f);
|
self.bits[offset] ^= 1 << (x & 0x1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -975,7 +975,7 @@ impl BitMatrix {
|
|||||||
* @param mask XOR mask
|
* @param mask XOR mask
|
||||||
*/
|
*/
|
||||||
pub fn xor(&mut self, mask: &BitMatrix) -> Result<(), Exceptions> {
|
pub fn xor(&mut self, mask: &BitMatrix) -> Result<(), Exceptions> {
|
||||||
if self.width != mask.width || self.height != mask.height || self.rowSize != mask.rowSize {
|
if self.width != mask.width || self.height != mask.height || self.row_size != mask.row_size {
|
||||||
return Err(Exceptions::IllegalArgumentException(
|
return Err(Exceptions::IllegalArgumentException(
|
||||||
"input matrix dimensions do not match".to_owned(),
|
"input matrix dimensions do not match".to_owned(),
|
||||||
));
|
));
|
||||||
@@ -983,10 +983,10 @@ impl BitMatrix {
|
|||||||
let rowArray = BitArray::with_size(self.width as usize);
|
let rowArray = BitArray::with_size(self.width as usize);
|
||||||
for y in 0..self.height {
|
for y in 0..self.height {
|
||||||
//for (int y = 0; y < height; y++) {
|
//for (int y = 0; y < height; y++) {
|
||||||
let offset = y as usize * self.rowSize;
|
let offset = y as usize * self.row_size;
|
||||||
let tmp = mask.getRow(y, &rowArray);
|
let tmp = mask.getRow(y, &rowArray);
|
||||||
let row = tmp.getBitArray();
|
let row = tmp.getBitArray();
|
||||||
for x in 0..self.rowSize {
|
for x in 0..self.row_size {
|
||||||
//for (int x = 0; x < rowSize; x++) {
|
//for (int x = 0; x < rowSize; x++) {
|
||||||
self.bits[offset + x] ^= row[x];
|
self.bits[offset + x] ^= row[x];
|
||||||
}
|
}
|
||||||
@@ -1039,7 +1039,7 @@ impl BitMatrix {
|
|||||||
}
|
}
|
||||||
for y in top..bottom {
|
for y in top..bottom {
|
||||||
//for (int y = top; y < bottom; y++) {
|
//for (int y = top; y < bottom; y++) {
|
||||||
let offset = y as usize * self.rowSize;
|
let offset = y as usize * self.row_size;
|
||||||
for x in left..right {
|
for x in left..right {
|
||||||
//for (int x = left; x < right; x++) {
|
//for (int x = left; x < right; x++) {
|
||||||
self.bits[offset + (x as usize / 32)] |= 1 << (x & 0x1f);
|
self.bits[offset + (x as usize / 32)] |= 1 << (x & 0x1f);
|
||||||
@@ -1067,8 +1067,8 @@ impl BitMatrix {
|
|||||||
// row.clone()
|
// row.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset = y as usize * self.rowSize;
|
let offset = y as usize * self.row_size;
|
||||||
for x in 0..self.rowSize {
|
for x in 0..self.row_size {
|
||||||
//for (int x = 0; x < rowSize; x++) {
|
//for (int x = 0; x < rowSize; x++) {
|
||||||
rw.setBulk(x * 32, self.bits[offset + x]);
|
rw.setBulk(x * 32, self.bits[offset + x]);
|
||||||
}
|
}
|
||||||
@@ -1080,8 +1080,8 @@ impl BitMatrix {
|
|||||||
* @param row {@link BitArray} to copy from
|
* @param row {@link BitArray} to copy from
|
||||||
*/
|
*/
|
||||||
pub fn setRow(&mut self, y: u32, row: &BitArray) {
|
pub fn setRow(&mut self, y: u32, row: &BitArray) {
|
||||||
return self.bits[y as usize * self.rowSize..y as usize * self.rowSize + self.rowSize]
|
return self.bits[y as usize * self.row_size..y as usize * self.row_size + self.row_size]
|
||||||
.clone_from_slice(&row.getBitArray()[0..self.rowSize]);
|
.clone_from_slice(&row.getBitArray()[0..self.row_size]);
|
||||||
//System.arraycopy(row.getBitArray(), 0, self.bits, y * self.rowSize, self.rowSize);
|
//System.arraycopy(row.getBitArray(), 0, self.bits, y * self.rowSize, self.rowSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1144,7 +1144,7 @@ impl BitMatrix {
|
|||||||
//for (int y = 0; y < height; y++) {
|
//for (int y = 0; y < height; y++) {
|
||||||
for x in 0..self.width {
|
for x in 0..self.width {
|
||||||
//for (int x = 0; x < width; x++) {
|
//for (int x = 0; x < width; x++) {
|
||||||
let offset = y as usize * self.rowSize + (x as usize / 32);
|
let offset = y as usize * self.row_size + (x as usize / 32);
|
||||||
if ((self.bits[offset] >> (x & 0x1f)) & 1) != 0 {
|
if ((self.bits[offset] >> (x & 0x1f)) & 1) != 0 {
|
||||||
let newOffset: usize = ((newHeight - 1 - x) * newRowSize + (y / 32))
|
let newOffset: usize = ((newHeight - 1 - x) * newRowSize + (y / 32))
|
||||||
.try_into()
|
.try_into()
|
||||||
@@ -1155,7 +1155,7 @@ impl BitMatrix {
|
|||||||
}
|
}
|
||||||
self.width = newWidth;
|
self.width = newWidth;
|
||||||
self.height = newHeight;
|
self.height = newHeight;
|
||||||
self.rowSize = newRowSize.try_into().unwrap();
|
self.row_size = newRowSize.try_into().unwrap();
|
||||||
self.bits = newBits;
|
self.bits = newBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1174,9 +1174,9 @@ impl BitMatrix {
|
|||||||
|
|
||||||
for y in 0..self.height {
|
for y in 0..self.height {
|
||||||
//for (int y = 0; y < height; y++) {
|
//for (int y = 0; y < height; y++) {
|
||||||
for x32 in 0..self.rowSize {
|
for x32 in 0..self.row_size {
|
||||||
//for (int x32 = 0; x32 < rowSize; x32++) {
|
//for (int x32 = 0; x32 < rowSize; x32++) {
|
||||||
let theBits = self.bits[y as usize * self.rowSize + x32];
|
let theBits = self.bits[y as usize * self.row_size + x32];
|
||||||
if theBits != 0 {
|
if theBits != 0 {
|
||||||
if y < top {
|
if y < top {
|
||||||
top = y;
|
top = y;
|
||||||
@@ -1226,8 +1226,8 @@ impl BitMatrix {
|
|||||||
if bitsOffset == self.bits.len() {
|
if bitsOffset == self.bits.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let y = bitsOffset / self.rowSize;
|
let y = bitsOffset / self.row_size;
|
||||||
let mut x = (bitsOffset % self.rowSize) * 32;
|
let mut x = (bitsOffset % self.row_size) * 32;
|
||||||
|
|
||||||
let theBits = self.bits[bitsOffset];
|
let theBits = self.bits[bitsOffset];
|
||||||
let mut bit = 0;
|
let mut bit = 0;
|
||||||
@@ -1247,8 +1247,8 @@ impl BitMatrix {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let y = bitsOffset as usize / self.rowSize;
|
let y = bitsOffset as usize / self.row_size;
|
||||||
let mut x = (bitsOffset as usize % self.rowSize) * 32;
|
let mut x = (bitsOffset as usize % self.row_size) * 32;
|
||||||
|
|
||||||
let theBits = self.bits[bitsOffset as usize];
|
let theBits = self.bits[bitsOffset as usize];
|
||||||
let mut bit = 31;
|
let mut bit = 31;
|
||||||
@@ -1278,7 +1278,7 @@ impl BitMatrix {
|
|||||||
* @return The row size of the matrix
|
* @return The row size of the matrix
|
||||||
*/
|
*/
|
||||||
pub fn getRowSize(&self) -> usize {
|
pub fn getRowSize(&self) -> usize {
|
||||||
return self.rowSize;
|
return self.row_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
|
|||||||
434
src/lib.rs
434
src/lib.rs
@@ -1,12 +1,12 @@
|
|||||||
|
mod aztec;
|
||||||
|
mod client;
|
||||||
mod common;
|
mod common;
|
||||||
mod exceptions;
|
mod exceptions;
|
||||||
mod client;
|
|
||||||
mod aztec;
|
|
||||||
|
|
||||||
#[cfg(feature="image")]
|
#[cfg(feature = "image")]
|
||||||
mod BufferedImageLuminanceSource;
|
mod BufferedImageLuminanceSource;
|
||||||
|
|
||||||
#[cfg(feature="image")]
|
#[cfg(feature = "image")]
|
||||||
pub use BufferedImageLuminanceSource::*;
|
pub use BufferedImageLuminanceSource::*;
|
||||||
|
|
||||||
use crate::common::{BitArray, BitMatrix};
|
use crate::common::{BitArray, BitMatrix};
|
||||||
@@ -45,8 +45,8 @@ mod RGBLuminanceSourceTestCase;
|
|||||||
*
|
*
|
||||||
* @author Sean Owen
|
* @author Sean Owen
|
||||||
*/
|
*/
|
||||||
#[derive(Debug,PartialEq, Eq,Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash,Clone, Copy)]
|
||||||
pub enum BarcodeFormat {
|
pub enum BarcodeFormat {
|
||||||
/** Aztec 2D barcode format. */
|
/** Aztec 2D barcode format. */
|
||||||
AZTEC,
|
AZTEC,
|
||||||
|
|
||||||
@@ -122,6 +122,7 @@ mod RGBLuminanceSourceTestCase;
|
|||||||
*
|
*
|
||||||
* @author dswitkin@google.com (Daniel Switkin)
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
*/
|
*/
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
pub enum EncodeHintType {
|
pub enum EncodeHintType {
|
||||||
/**
|
/**
|
||||||
* Specifies what degree of error correction to use, for example in QR Codes.
|
* Specifies what degree of error correction to use, for example in QR Codes.
|
||||||
@@ -274,6 +275,158 @@ pub enum EncodeHintType {
|
|||||||
CODE128_COMPACT,
|
CODE128_COMPACT,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum EncodeHintValue {
|
||||||
|
/**
|
||||||
|
* Specifies what degree of error correction to use, for example in QR Codes.
|
||||||
|
* Type depends on the encoder. For example for QR codes it's type
|
||||||
|
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
|
||||||
|
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
|
||||||
|
* For PDF417 it is of type {@link Integer}, valid values being 0 to 8.
|
||||||
|
* In all cases, it can also be a {@link String} representation of the desired value as well.
|
||||||
|
* Note: an Aztec symbol should have a minimum of 25% EC words.
|
||||||
|
*/
|
||||||
|
ErrorCorrection(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what character encoding to use where applicable (type {@link String})
|
||||||
|
*/
|
||||||
|
CharacterSet(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
|
||||||
|
*/
|
||||||
|
DataMatrixShape,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether to use compact mode for Data Matrix (type {@link Boolean}, or "true" or "false"
|
||||||
|
* {@link String } value).
|
||||||
|
* The compact encoding mode also supports the encoding of characters that are not in the ISO-8859-1
|
||||||
|
* character set via ECIs.
|
||||||
|
* Please note that in that case, the most compact character encoding is chosen for characters in
|
||||||
|
* the input that are not in the ISO-8859-1 character set. Based on experience, some scanners do not
|
||||||
|
* support encodings like cp-1256 (Arabic). In such cases the encoding can be forced to UTF-8 by
|
||||||
|
* means of the {@link #CHARACTER_SET} encoding hint.
|
||||||
|
* Compact encoding also provides GS1-FNC1 support when {@link #GS1_FORMAT} is selected. In this case
|
||||||
|
* group-separator character (ASCII 29 decimal) can be used to encode the positions of FNC1 codewords
|
||||||
|
* for the purpose of delimiting AIs.
|
||||||
|
* This option and {@link #FORCE_C40} are mutually exclusive.
|
||||||
|
*/
|
||||||
|
DataMatrixCompact(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||||
|
*
|
||||||
|
* @deprecated use width/height params in
|
||||||
|
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
|
||||||
|
*/
|
||||||
|
#[deprecated]
|
||||||
|
MinSize,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||||
|
*
|
||||||
|
* @deprecated without replacement
|
||||||
|
*/
|
||||||
|
#[deprecated]
|
||||||
|
MaxSize(Dimension),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
|
||||||
|
* by format; for example it controls margin before and after the barcode horizontally for
|
||||||
|
* most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||||
|
*/
|
||||||
|
Margin(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false"
|
||||||
|
* {@link String} value).
|
||||||
|
*/
|
||||||
|
Pdf417Compact(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what compaction mode to use for PDF417 (type
|
||||||
|
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its
|
||||||
|
* enum values).
|
||||||
|
*/
|
||||||
|
Pdf417Compaction(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
|
||||||
|
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
|
||||||
|
*/
|
||||||
|
Pdf417Dimensions,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether to automatically insert ECIs when encoding PDF417 (type {@link Boolean}, or "true" or "false"
|
||||||
|
* {@link String} value).
|
||||||
|
* Please note that in that case, the most compact character encoding is chosen for characters in
|
||||||
|
* the input that are not in the ISO-8859-1 character set. Based on experience, some scanners do not
|
||||||
|
* support encodings like cp-1256 (Arabic). In such cases the encoding can be forced to UTF-8 by
|
||||||
|
* means of the {@link #CHARACTER_SET} encoding hint.
|
||||||
|
*/
|
||||||
|
Pdf417AutoEci(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the required number of layers for an Aztec code.
|
||||||
|
* A negative number (-1, -2, -3, -4) specifies a compact Aztec code.
|
||||||
|
* 0 indicates to use the minimum number of layers (the default).
|
||||||
|
* A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.
|
||||||
|
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||||
|
*/
|
||||||
|
AztecLayers(u32),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the exact version of QR code to be encoded.
|
||||||
|
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||||
|
*/
|
||||||
|
QrVersion(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the QR code mask pattern to be used. Allowed values are
|
||||||
|
* 0..QRCode.NUM_MASK_PATTERNS-1. By default the code will automatically select
|
||||||
|
* the optimal mask pattern.
|
||||||
|
* * (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||||
|
*/
|
||||||
|
QrMaskPattern(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether to use compact mode for QR code (type {@link Boolean}, or "true" or "false"
|
||||||
|
* {@link String } value).
|
||||||
|
* Please note that when compaction is performed, the most compact character encoding is chosen
|
||||||
|
* for characters in the input that are not in the ISO-8859-1 character set. Based on experience,
|
||||||
|
* some scanners do not support encodings like cp-1256 (Arabic). In such cases the encoding can
|
||||||
|
* be forced to UTF-8 by means of the {@link #CHARACTER_SET} encoding hint.
|
||||||
|
*/
|
||||||
|
QrCompact(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or "true" or "false"
|
||||||
|
* {@link String } value).
|
||||||
|
*/
|
||||||
|
Gs1Format(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces which encoding will be used. Currently only used for Code-128 code sets (Type {@link String}).
|
||||||
|
* Valid values are "A", "B", "C".
|
||||||
|
* This option and {@link #CODE128_COMPACT} are mutually exclusive.
|
||||||
|
*/
|
||||||
|
ForceCodeSet(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces C40 encoding for data-matrix (type {@link Boolean}, or "true" or "false") {@link String } value). This
|
||||||
|
* option and {@link #DATA_MATRIX_COMPACT} are mutually exclusive.
|
||||||
|
*/
|
||||||
|
ForceC40(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether to use compact mode for Code-128 code (type {@link Boolean}, or "true" or "false"
|
||||||
|
* {@link String } value).
|
||||||
|
* This can yield slightly smaller bar codes. This option and {@link #FORCE_CODE_SET} are mutually
|
||||||
|
* exclusive.
|
||||||
|
*/
|
||||||
|
Code128Compact(String),
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2007 ZXing authors
|
* Copyright 2007 ZXing authors
|
||||||
*
|
*
|
||||||
@@ -398,6 +551,91 @@ pub enum DecodeHintType {
|
|||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback which is invoked when a possible result point (significant
|
||||||
|
* point in the barcode image such as a corner) is found.
|
||||||
|
*
|
||||||
|
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
|
||||||
|
*/
|
||||||
|
pub type RXingResultPointCallback = fn(&RXingResultPoint);
|
||||||
|
|
||||||
|
pub enum DecodeHintValue {
|
||||||
|
/**
|
||||||
|
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
|
||||||
|
*/
|
||||||
|
Other(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
|
||||||
|
* use {@link Boolean#TRUE}.
|
||||||
|
*/
|
||||||
|
PureBarcode(bool),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image is known to be of one of a few possible formats.
|
||||||
|
* Maps to a {@link List} of {@link BarcodeFormat}s.
|
||||||
|
*/
|
||||||
|
PossibleFormats(BarcodeFormat),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spend more time to try to find a barcode; optimize for accuracy, not speed.
|
||||||
|
* Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||||
|
*/
|
||||||
|
TryHarder(bool),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies what character encoding to use when decoding, where applicable (type String)
|
||||||
|
*/
|
||||||
|
CharacterSet(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.
|
||||||
|
*/
|
||||||
|
AllowedLengths(Vec<u32>),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;
|
||||||
|
* use {@link Boolean#TRUE}.
|
||||||
|
*/
|
||||||
|
AssumeCode39CheckDigit(bool),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
|
||||||
|
* For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
|
||||||
|
* use {@link Boolean#TRUE}.
|
||||||
|
*/
|
||||||
|
AssumeGs1(bool),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, return the start and end digits in a Codabar barcode instead of stripping them. They
|
||||||
|
* are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
|
||||||
|
* to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||||
|
*/
|
||||||
|
ReturnCodabarStartEnd(bool),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The caller needs to be notified via callback when a possible {@link RXingResultPoint}
|
||||||
|
* is found. Maps to a {@link RXingResultPointCallback}.
|
||||||
|
*/
|
||||||
|
NeedResultPointCallback(RXingResultPointCallback),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
|
||||||
|
* Maps to an {@code int[]} of the allowed extension lengths, for example [2], [5], or [2, 5].
|
||||||
|
* If it is optional to have an extension, do not set this hint. If this is set,
|
||||||
|
* and a UPC or EAN barcode is found but an extension is not, then no result will be returned
|
||||||
|
* at all.
|
||||||
|
*/
|
||||||
|
AllowedEanExtensions(Vec<u32>),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, also tries to decode as inverted image. All configured decoders are simply called a
|
||||||
|
* second time with an inverted image. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||||
|
*/
|
||||||
|
AlsoInverted(bool),
|
||||||
|
// End of enumeration values.
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2008 ZXing authors
|
* Copyright 2008 ZXing authors
|
||||||
*
|
*
|
||||||
@@ -448,12 +686,12 @@ pub trait Writer {
|
|||||||
* @return {@link BitMatrix} representing encoded barcode image
|
* @return {@link BitMatrix} representing encoded barcode image
|
||||||
* @throws WriterException if contents cannot be encoded legally in a format
|
* @throws WriterException if contents cannot be encoded legally in a format
|
||||||
*/
|
*/
|
||||||
fn encode_with_hints<T>(
|
fn encode_with_hints(
|
||||||
contents: &str,
|
contents: &str,
|
||||||
format: &BarcodeFormat,
|
format: &BarcodeFormat,
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
hints: HashMap<EncodeHintType, T>,
|
hints: &HashMap<EncodeHintType, EncodeHintValue>,
|
||||||
) -> Result<BitMatrix, Exceptions>;
|
) -> Result<BitMatrix, Exceptions>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,8 +713,6 @@ pub trait Writer {
|
|||||||
|
|
||||||
//package com.google.zxing;
|
//package com.google.zxing;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementations of this interface can decode an image of a barcode in some format into
|
* Implementations of this interface can decode an image of a barcode in some format into
|
||||||
* the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
|
* the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
|
||||||
@@ -499,7 +735,7 @@ pub trait Reader {
|
|||||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||||
* @throws FormatException if a potential barcode is found but format is invalid
|
* @throws FormatException if a potential barcode is found but format is invalid
|
||||||
*/
|
*/
|
||||||
fn decode(image: BinaryBitmap) -> Result<RXingResult, Exceptions>;
|
fn decode(image: &BinaryBitmap) -> Result<RXingResult, Exceptions>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locates and decodes a barcode in some format within an image. This method also accepts
|
* Locates and decodes a barcode in some format within an image. This method also accepts
|
||||||
@@ -515,9 +751,9 @@ pub trait Reader {
|
|||||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||||
* @throws FormatException if a potential barcode is found but format is invalid
|
* @throws FormatException if a potential barcode is found but format is invalid
|
||||||
*/
|
*/
|
||||||
fn decode_with_hints<T>(
|
fn decode_with_hints(
|
||||||
image: BinaryBitmap,
|
image: &BinaryBitmap,
|
||||||
hints: HashMap<DecodeHintType, T>,
|
hints: &HashMap<DecodeHintType, DecodeHintValue>,
|
||||||
) -> Result<RXingResult, Exceptions>;
|
) -> Result<RXingResult, Exceptions>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -631,6 +867,85 @@ pub enum RXingResultMetadataType {
|
|||||||
SYMBOLOGY_IDENTIFIER,
|
SYMBOLOGY_IDENTIFIER,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum RXingResultMetadataValue {
|
||||||
|
/**
|
||||||
|
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
|
||||||
|
*/
|
||||||
|
OTHER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Denotes the likely approximate orientation of the barcode in the image. This value
|
||||||
|
* is given as degrees rotated clockwise from the normal, upright orientation.
|
||||||
|
* For example a 1D barcode which was found by reading top-to-bottom would be
|
||||||
|
* said to have orientation "90". This key maps to an {@link Integer} whose
|
||||||
|
* value is in the range [0,360).
|
||||||
|
*/
|
||||||
|
Orientation(i32),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
|
||||||
|
* which is sometimes used to encode binary data. While {@link RXingResult} makes available
|
||||||
|
* the complete raw bytes in the barcode for these formats, it does not offer the bytes
|
||||||
|
* from the byte segments alone.</p>
|
||||||
|
*
|
||||||
|
* <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
|
||||||
|
* raw bytes in the byte segments in the barcode, in order.</p>
|
||||||
|
*/
|
||||||
|
ByteSegments(Vec<u8>),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error correction level used, if applicable. The value type depends on the
|
||||||
|
* format, but is typically a String.
|
||||||
|
*/
|
||||||
|
ErrorCorrectionLevel(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some periodicals, indicates the issue number as an {@link Integer}.
|
||||||
|
*/
|
||||||
|
IssueNumber(i32),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some products, indicates the suggested retail price in the barcode as a
|
||||||
|
* formatted {@link String}.
|
||||||
|
*/
|
||||||
|
SuggestedPrice(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some products, the possible country of manufacture as a {@link String} denoting the
|
||||||
|
* ISO country code. Some map to multiple possible countries, like "US/CA".
|
||||||
|
*/
|
||||||
|
PossibleCountry(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some products, the extension text
|
||||||
|
*/
|
||||||
|
UpcEanExtension(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDF417-specific metadata
|
||||||
|
*/
|
||||||
|
Pdf417ExtraMetadata(String),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the code format supports structured append and the current scanned code is part of one then the
|
||||||
|
* sequence number is given with it.
|
||||||
|
*/
|
||||||
|
StructuredAppendSequence(u32),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the code format supports structured append and the current scanned code is part of one then the
|
||||||
|
* parity is given with it.
|
||||||
|
*/
|
||||||
|
StructuredAppendParity(u32),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Barcode Symbology Identifier.
|
||||||
|
* Note: According to the GS1 specification the identifier may have to replace a leading FNC1/GS character
|
||||||
|
* when prepending to the barcode content.
|
||||||
|
*/
|
||||||
|
SymbologyIdentifier(String),
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2007 ZXing authors
|
* Copyright 2007 ZXing authors
|
||||||
*
|
*
|
||||||
@@ -663,7 +978,7 @@ pub struct RXingResult {
|
|||||||
numBits: usize,
|
numBits: usize,
|
||||||
resultPoints: Vec<RXingResultPoint>,
|
resultPoints: Vec<RXingResultPoint>,
|
||||||
format: BarcodeFormat,
|
format: BarcodeFormat,
|
||||||
resultMetadata: HashMap<RXingResultMetadataType, String>,
|
resultMetadata: HashMap<RXingResultMetadataType, RXingResultMetadataValue>,
|
||||||
timestamp: u128,
|
timestamp: u128,
|
||||||
}
|
}
|
||||||
impl RXingResult {
|
impl RXingResult {
|
||||||
@@ -758,15 +1073,24 @@ impl RXingResult {
|
|||||||
* {@code null}. This contains optional metadata about what was detected about the barcode,
|
* {@code null}. This contains optional metadata about what was detected about the barcode,
|
||||||
* like orientation.
|
* like orientation.
|
||||||
*/
|
*/
|
||||||
pub fn getRXingResultMetadata(&self) -> &HashMap<RXingResultMetadataType, String> {
|
pub fn getRXingResultMetadata(
|
||||||
|
&self,
|
||||||
|
) -> &HashMap<RXingResultMetadataType, RXingResultMetadataValue> {
|
||||||
return &self.resultMetadata;
|
return &self.resultMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn putMetadata(&mut self, md_type: RXingResultMetadataType, value: String) {
|
pub fn putMetadata(
|
||||||
|
&mut self,
|
||||||
|
md_type: RXingResultMetadataType,
|
||||||
|
value: RXingResultMetadataValue,
|
||||||
|
) {
|
||||||
self.resultMetadata.insert(md_type, value);
|
self.resultMetadata.insert(md_type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn putAllMetadata(&mut self, metadata: HashMap<RXingResultMetadataType, String>) {
|
pub fn putAllMetadata(
|
||||||
|
&mut self,
|
||||||
|
metadata: HashMap<RXingResultMetadataType, RXingResultMetadataValue>,
|
||||||
|
) {
|
||||||
if self.resultMetadata.is_empty() {
|
if self.resultMetadata.is_empty() {
|
||||||
self.resultMetadata = metadata;
|
self.resultMetadata = metadata;
|
||||||
} else {
|
} else {
|
||||||
@@ -1248,34 +1572,6 @@ impl fmt::Display for BinaryBitmap {
|
|||||||
|
|
||||||
//package com.google.zxing;
|
//package com.google.zxing;
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback which is invoked when a possible result point (significant
|
|
||||||
* point in the barcode image such as a corner) is found.
|
|
||||||
*
|
|
||||||
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
|
|
||||||
*/
|
|
||||||
pub trait RXingResultPointCallback {
|
|
||||||
fn foundPossibleRXingResultPoint(point: RXingResultPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2009 ZXing authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//package com.google.zxing;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The purpose of this class hierarchy is to abstract different bitmap implementations across
|
* The purpose of this class hierarchy is to abstract different bitmap implementations across
|
||||||
* platforms into a standard interface for requesting greyscale luminance values. The interface
|
* platforms into a standard interface for requesting greyscale luminance values. The interface
|
||||||
@@ -1375,9 +1671,7 @@ pub trait LuminanceSource {
|
|||||||
*
|
*
|
||||||
* @return A rotated version of this object.
|
* @return A rotated version of this object.
|
||||||
*/
|
*/
|
||||||
fn rotateCounterClockwise(
|
fn rotateCounterClockwise(&self) -> Result<Box<dyn LuminanceSource>, Exceptions> {
|
||||||
&self,
|
|
||||||
) -> Result<Box<dyn LuminanceSource>, Exceptions> {
|
|
||||||
return Err(Exceptions::UnsupportedOperationException(
|
return Err(Exceptions::UnsupportedOperationException(
|
||||||
"This luminance source does not support rotation by 90 degrees.".to_owned(),
|
"This luminance source does not support rotation by 90 degrees.".to_owned(),
|
||||||
));
|
));
|
||||||
@@ -1389,9 +1683,7 @@ pub trait LuminanceSource {
|
|||||||
*
|
*
|
||||||
* @return A rotated version of this object.
|
* @return A rotated version of this object.
|
||||||
*/
|
*/
|
||||||
fn rotateCounterClockwise45(
|
fn rotateCounterClockwise45(&self) -> Result<Box<dyn LuminanceSource>, Exceptions> {
|
||||||
&self,
|
|
||||||
) -> Result<Box<dyn LuminanceSource>, Exceptions> {
|
|
||||||
return Err(Exceptions::UnsupportedOperationException(
|
return Err(Exceptions::UnsupportedOperationException(
|
||||||
"This luminance source does not support rotation by 45 degrees.".to_owned(),
|
"This luminance source does not support rotation by 45 degrees.".to_owned(),
|
||||||
));
|
));
|
||||||
@@ -1624,7 +1916,7 @@ impl PlanarYUVLuminanceSource {
|
|||||||
pub fn renderThumbnail(&self) -> Vec<u8> {
|
pub fn renderThumbnail(&self) -> Vec<u8> {
|
||||||
let width = self.getWidth() / THUMBNAIL_SCALE_FACTOR;
|
let width = self.getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||||
let height = self.getHeight() / THUMBNAIL_SCALE_FACTOR;
|
let height = self.getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||||
let mut pixels = vec![0;width * height];
|
let mut pixels = vec![0; width * height];
|
||||||
let yuv = &self.yuv_data;
|
let yuv = &self.yuv_data;
|
||||||
let mut input_offset = self.top * self.data_width + self.left;
|
let mut input_offset = self.top * self.data_width + self.left;
|
||||||
|
|
||||||
@@ -1692,13 +1984,13 @@ impl LuminanceSource for PlanarYUVLuminanceSource {
|
|||||||
|
|
||||||
let offset = (y + self.top) * self.data_width + self.left;
|
let offset = (y + self.top) * self.data_width + self.left;
|
||||||
|
|
||||||
let mut row = if row.len() >= width{
|
let mut row = if row.len() >= width {
|
||||||
row.to_vec()}
|
row.to_vec()
|
||||||
else{
|
} else {
|
||||||
vec![0;width]
|
vec![0; width]
|
||||||
};
|
};
|
||||||
|
|
||||||
row[..width].clone_from_slice(&self.yuv_data[offset..width+offset]);
|
row[..width].clone_from_slice(&self.yuv_data[offset..width + offset]);
|
||||||
//System.arraycopy(yuvData, offset, row, 0, width);
|
//System.arraycopy(yuvData, offset, row, 0, width);
|
||||||
if self.invert {
|
if self.invert {
|
||||||
row = self.invert_block_of_bytes(row);
|
row = self.invert_block_of_bytes(row);
|
||||||
@@ -1721,7 +2013,7 @@ impl LuminanceSource for PlanarYUVLuminanceSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let area = width * height;
|
let area = width * height;
|
||||||
let mut matrix = vec![0;area];
|
let mut matrix = vec![0; area];
|
||||||
let mut inputOffset = self.top * self.data_width + self.left;
|
let mut inputOffset = self.top * self.data_width + self.left;
|
||||||
|
|
||||||
// If the width matches the full width of the underlying data, perform a single copy.
|
// If the width matches the full width of the underlying data, perform a single copy.
|
||||||
@@ -1738,7 +2030,8 @@ impl LuminanceSource for PlanarYUVLuminanceSource {
|
|||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
//for (int y = 0; y < height; y++) {
|
//for (int y = 0; y < height; y++) {
|
||||||
let outputOffset = y * width;
|
let outputOffset = y * width;
|
||||||
matrix[outputOffset..outputOffset+width].clone_from_slice(&self.yuv_data[inputOffset..inputOffset+width]);
|
matrix[outputOffset..outputOffset + width]
|
||||||
|
.clone_from_slice(&self.yuv_data[inputOffset..inputOffset + width]);
|
||||||
//System.arraycopy(yuvData, inputOffset, matrix, outputOffset, width);
|
//System.arraycopy(yuvData, inputOffset, matrix, outputOffset, width);
|
||||||
inputOffset += self.data_width;
|
inputOffset += self.data_width;
|
||||||
}
|
}
|
||||||
@@ -1841,12 +2134,12 @@ impl LuminanceSource for RGBLuminanceSource {
|
|||||||
let offset = (y + self.top) * self.dataWidth + self.left;
|
let offset = (y + self.top) * self.dataWidth + self.left;
|
||||||
|
|
||||||
let mut row = if row.len() >= width {
|
let mut row = if row.len() >= width {
|
||||||
row.to_vec()
|
row.to_vec()
|
||||||
}else{
|
} else {
|
||||||
vec![0;width]
|
vec![0; width]
|
||||||
};
|
};
|
||||||
|
|
||||||
row[..width].clone_from_slice(&self.luminances[offset..offset+width]);
|
row[..width].clone_from_slice(&self.luminances[offset..offset + width]);
|
||||||
//System.arraycopy(self.luminances, offset, row, 0, width);
|
//System.arraycopy(self.luminances, offset, row, 0, width);
|
||||||
if self.invert {
|
if self.invert {
|
||||||
row = self.invert_block_of_bytes(row);
|
row = self.invert_block_of_bytes(row);
|
||||||
@@ -1869,12 +2162,12 @@ impl LuminanceSource for RGBLuminanceSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let area = width * height;
|
let area = width * height;
|
||||||
let mut matrix = vec![0;area];
|
let mut matrix = vec![0; area];
|
||||||
let mut inputOffset = self.top * self.dataWidth + self.left;
|
let mut inputOffset = self.top * self.dataWidth + self.left;
|
||||||
|
|
||||||
// If the width matches the full width of the underlying data, perform a single copy.
|
// If the width matches the full width of the underlying data, perform a single copy.
|
||||||
if width == self.dataWidth {
|
if width == self.dataWidth {
|
||||||
matrix[..area].clone_from_slice(&self.luminances[inputOffset..area+inputOffset]);
|
matrix[..area].clone_from_slice(&self.luminances[inputOffset..area + inputOffset]);
|
||||||
//System.arraycopy(self.luminances, inputOffset, matrix, 0, area);
|
//System.arraycopy(self.luminances, inputOffset, matrix, 0, area);
|
||||||
if self.invert {
|
if self.invert {
|
||||||
matrix = self.invert_block_of_bytes(matrix);
|
matrix = self.invert_block_of_bytes(matrix);
|
||||||
@@ -1886,7 +2179,8 @@ impl LuminanceSource for RGBLuminanceSource {
|
|||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
//for (int y = 0; y < height; y++) {
|
//for (int y = 0; y < height; y++) {
|
||||||
let outputOffset = y * width;
|
let outputOffset = y * width;
|
||||||
matrix[outputOffset..width+outputOffset].clone_from_slice(&self.luminances[inputOffset..width+inputOffset]);
|
matrix[outputOffset..width + outputOffset]
|
||||||
|
.clone_from_slice(&self.luminances[inputOffset..width + inputOffset]);
|
||||||
//System.arraycopy(luminances, inputOffset, matrix, outputOffset, width);
|
//System.arraycopy(luminances, inputOffset, matrix, outputOffset, width);
|
||||||
inputOffset += self.dataWidth;
|
inputOffset += self.dataWidth;
|
||||||
}
|
}
|
||||||
@@ -1949,11 +2243,11 @@ impl RGBLuminanceSource {
|
|||||||
//
|
//
|
||||||
// Total number of pixels suffices, can ignore shape
|
// Total number of pixels suffices, can ignore shape
|
||||||
let size = width * height;
|
let size = width * height;
|
||||||
let mut luminances: Vec<u8> = vec![0;size];
|
let mut luminances: Vec<u8> = vec![0; size];
|
||||||
for offset in 0..size {
|
for offset in 0..size {
|
||||||
//for (int offset = 0; offset < size; offset++) {
|
//for (int offset = 0; offset < size; offset++) {
|
||||||
let pixel = pixels[offset];
|
let pixel = pixels[offset];
|
||||||
let r = (pixel >> 16) & 0xff ; // red
|
let r = (pixel >> 16) & 0xff; // red
|
||||||
let g2 = (pixel >> 7) & 0x1fe; // 2 * green
|
let g2 = (pixel >> 7) & 0x1fe; // 2 * green
|
||||||
let b = pixel & 0xff; // blue
|
let b = pixel & 0xff; // blue
|
||||||
// Calculate green-favouring average cheaply
|
// Calculate green-favouring average cheaply
|
||||||
|
|||||||
Reference in New Issue
Block a user