mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 12:22:34 +00:00
porting encoders
This commit is contained in:
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 Jeremias Maerki.
|
||||
*
|
||||
* 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.datamatrix.encoder;
|
||||
|
||||
final class ASCIIEncoder implements Encoder {
|
||||
|
||||
@Override
|
||||
public int getEncodingMode() {
|
||||
return HighLevelEncoder.ASCII_ENCODATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(EncoderContext context) {
|
||||
//step B
|
||||
int n = HighLevelEncoder.determineConsecutiveDigitCount(context.getMessage(), context.pos);
|
||||
if (n >= 2) {
|
||||
context.writeCodeword(encodeASCIIDigits(context.getMessage().charAt(context.pos),
|
||||
context.getMessage().charAt(context.pos + 1)));
|
||||
context.pos += 2;
|
||||
} else {
|
||||
char c = context.getCurrentChar();
|
||||
int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());
|
||||
if (newMode != getEncodingMode()) {
|
||||
switch (newMode) {
|
||||
case HighLevelEncoder.BASE256_ENCODATION:
|
||||
context.writeCodeword(HighLevelEncoder.LATCH_TO_BASE256);
|
||||
context.signalEncoderChange(HighLevelEncoder.BASE256_ENCODATION);
|
||||
return;
|
||||
case HighLevelEncoder.C40_ENCODATION:
|
||||
context.writeCodeword(HighLevelEncoder.LATCH_TO_C40);
|
||||
context.signalEncoderChange(HighLevelEncoder.C40_ENCODATION);
|
||||
return;
|
||||
case HighLevelEncoder.X12_ENCODATION:
|
||||
context.writeCodeword(HighLevelEncoder.LATCH_TO_ANSIX12);
|
||||
context.signalEncoderChange(HighLevelEncoder.X12_ENCODATION);
|
||||
break;
|
||||
case HighLevelEncoder.TEXT_ENCODATION:
|
||||
context.writeCodeword(HighLevelEncoder.LATCH_TO_TEXT);
|
||||
context.signalEncoderChange(HighLevelEncoder.TEXT_ENCODATION);
|
||||
break;
|
||||
case HighLevelEncoder.EDIFACT_ENCODATION:
|
||||
context.writeCodeword(HighLevelEncoder.LATCH_TO_EDIFACT);
|
||||
context.signalEncoderChange(HighLevelEncoder.EDIFACT_ENCODATION);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Illegal mode: " + newMode);
|
||||
}
|
||||
} else if (HighLevelEncoder.isExtendedASCII(c)) {
|
||||
context.writeCodeword(HighLevelEncoder.UPPER_SHIFT);
|
||||
context.writeCodeword((char) (c - 128 + 1));
|
||||
context.pos++;
|
||||
} else {
|
||||
context.writeCodeword((char) (c + 1));
|
||||
context.pos++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static char encodeASCIIDigits(char digit1, char digit2) {
|
||||
if (HighLevelEncoder.isDigit(digit1) && HighLevelEncoder.isDigit(digit2)) {
|
||||
int num = (digit1 - 48) * 10 + (digit2 - 48);
|
||||
return (char) (num + 130);
|
||||
}
|
||||
throw new IllegalArgumentException("not digits: " + digit1 + digit2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 Jeremias Maerki.
|
||||
*
|
||||
* 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.datamatrix.encoder;
|
||||
|
||||
class C40Encoder implements Encoder {
|
||||
|
||||
@Override
|
||||
public int getEncodingMode() {
|
||||
return HighLevelEncoder.C40_ENCODATION;
|
||||
}
|
||||
|
||||
void encodeMaximal(EncoderContext context) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
int lastCharSize = 0;
|
||||
int backtrackStartPosition = context.pos;
|
||||
int backtrackBufferLength = 0;
|
||||
while (context.hasMoreCharacters()) {
|
||||
char c = context.getCurrentChar();
|
||||
context.pos++;
|
||||
lastCharSize = encodeChar(c, buffer);
|
||||
if (buffer.length() % 3 == 0) {
|
||||
backtrackStartPosition = context.pos;
|
||||
backtrackBufferLength = buffer.length();
|
||||
}
|
||||
}
|
||||
if (backtrackBufferLength != buffer.length()) {
|
||||
int unwritten = (buffer.length() / 3) * 2;
|
||||
|
||||
int curCodewordCount = context.getCodewordCount() + unwritten + 1; // +1 for the latch to C40
|
||||
context.updateSymbolInfo(curCodewordCount);
|
||||
int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
|
||||
int rest = buffer.length() % 3;
|
||||
if ((rest == 2 && available != 2) ||
|
||||
(rest == 1 && (lastCharSize > 3 || available != 1))) {
|
||||
buffer.setLength(backtrackBufferLength);
|
||||
context.pos = backtrackStartPosition;
|
||||
}
|
||||
}
|
||||
if (buffer.length() > 0) {
|
||||
context.writeCodeword(HighLevelEncoder.LATCH_TO_C40);
|
||||
}
|
||||
|
||||
handleEOD(context, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(EncoderContext context) {
|
||||
//step C
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
while (context.hasMoreCharacters()) {
|
||||
char c = context.getCurrentChar();
|
||||
context.pos++;
|
||||
|
||||
int lastCharSize = encodeChar(c, buffer);
|
||||
|
||||
int unwritten = (buffer.length() / 3) * 2;
|
||||
|
||||
int curCodewordCount = context.getCodewordCount() + unwritten;
|
||||
context.updateSymbolInfo(curCodewordCount);
|
||||
int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
|
||||
|
||||
if (!context.hasMoreCharacters()) {
|
||||
//Avoid having a single C40 value in the last triplet
|
||||
StringBuilder removed = new StringBuilder();
|
||||
if ((buffer.length() % 3) == 2 && available != 2) {
|
||||
lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);
|
||||
}
|
||||
while ((buffer.length() % 3) == 1 && (lastCharSize > 3 || available != 1)) {
|
||||
lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int count = buffer.length();
|
||||
if ((count % 3) == 0) {
|
||||
int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());
|
||||
if (newMode != getEncodingMode()) {
|
||||
// Return to ASCII encodation, which will actually handle latch to new mode
|
||||
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
handleEOD(context, buffer);
|
||||
}
|
||||
|
||||
private int backtrackOneCharacter(EncoderContext context,
|
||||
StringBuilder buffer, StringBuilder removed, int lastCharSize) {
|
||||
int count = buffer.length();
|
||||
buffer.delete(count - lastCharSize, count);
|
||||
context.pos--;
|
||||
char c = context.getCurrentChar();
|
||||
lastCharSize = encodeChar(c, removed);
|
||||
context.resetSymbolInfo(); //Deal with possible reduction in symbol size
|
||||
return lastCharSize;
|
||||
}
|
||||
|
||||
static void writeNextTriplet(EncoderContext context, StringBuilder buffer) {
|
||||
context.writeCodewords(encodeToCodewords(buffer));
|
||||
buffer.delete(0, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle "end of data" situations
|
||||
*
|
||||
* @param context the encoder context
|
||||
* @param buffer the buffer with the remaining encoded characters
|
||||
*/
|
||||
void handleEOD(EncoderContext context, StringBuilder buffer) {
|
||||
int unwritten = (buffer.length() / 3) * 2;
|
||||
int rest = buffer.length() % 3;
|
||||
|
||||
int curCodewordCount = context.getCodewordCount() + unwritten;
|
||||
context.updateSymbolInfo(curCodewordCount);
|
||||
int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
|
||||
|
||||
if (rest == 2) {
|
||||
buffer.append('\0'); //Shift 1
|
||||
while (buffer.length() >= 3) {
|
||||
writeNextTriplet(context, buffer);
|
||||
}
|
||||
if (context.hasMoreCharacters()) {
|
||||
context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
|
||||
}
|
||||
} else if (available == 1 && rest == 1) {
|
||||
while (buffer.length() >= 3) {
|
||||
writeNextTriplet(context, buffer);
|
||||
}
|
||||
if (context.hasMoreCharacters()) {
|
||||
context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
|
||||
}
|
||||
// else no unlatch
|
||||
context.pos--;
|
||||
} else if (rest == 0) {
|
||||
while (buffer.length() >= 3) {
|
||||
writeNextTriplet(context, buffer);
|
||||
}
|
||||
if (available > 0 || context.hasMoreCharacters()) {
|
||||
context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected case. Please report!");
|
||||
}
|
||||
context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
|
||||
}
|
||||
|
||||
int encodeChar(char c, StringBuilder sb) {
|
||||
if (c == ' ') {
|
||||
sb.append('\3');
|
||||
return 1;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
sb.append((char) (c - 48 + 4));
|
||||
return 1;
|
||||
}
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
sb.append((char) (c - 65 + 14));
|
||||
return 1;
|
||||
}
|
||||
if (c < ' ') {
|
||||
sb.append('\0'); //Shift 1 Set
|
||||
sb.append(c);
|
||||
return 2;
|
||||
}
|
||||
if (c <= '/') {
|
||||
sb.append('\1'); //Shift 2 Set
|
||||
sb.append((char) (c - 33));
|
||||
return 2;
|
||||
}
|
||||
if (c <= '@') {
|
||||
sb.append('\1'); //Shift 2 Set
|
||||
sb.append((char) (c - 58 + 15));
|
||||
return 2;
|
||||
}
|
||||
if (c <= '_') {
|
||||
sb.append('\1'); //Shift 2 Set
|
||||
sb.append((char) (c - 91 + 22));
|
||||
return 2;
|
||||
}
|
||||
if (c <= 127) {
|
||||
sb.append('\2'); //Shift 3 Set
|
||||
sb.append((char) (c - 96));
|
||||
return 2;
|
||||
}
|
||||
sb.append("\1\u001e"); //Shift 2, Upper Shift
|
||||
int len = 2;
|
||||
len += encodeChar((char) (c - 128), sb);
|
||||
return len;
|
||||
}
|
||||
|
||||
private static String encodeToCodewords(CharSequence sb) {
|
||||
int v = (1600 * sb.charAt(0)) + (40 * sb.charAt(1)) + sb.charAt(2) + 1;
|
||||
char cw1 = (char) (v / 256);
|
||||
char cw2 = (char) (v % 256);
|
||||
return new String(new char[] {cw1, cw2});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,484 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 Jeremias Maerki.
|
||||
*
|
||||
* 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.datamatrix.encoder;
|
||||
|
||||
import com.google.zxing.Dimension;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* DataMatrix ECC 200 data encoder following the algorithm described in ISO/IEC 16022:200(E) in
|
||||
* annex S.
|
||||
*/
|
||||
public final class HighLevelEncoder {
|
||||
|
||||
/**
|
||||
* Padding character
|
||||
*/
|
||||
private static final char PAD = 129;
|
||||
/**
|
||||
* mode latch to C40 encodation mode
|
||||
*/
|
||||
static final char LATCH_TO_C40 = 230;
|
||||
/**
|
||||
* mode latch to Base 256 encodation mode
|
||||
*/
|
||||
static final char LATCH_TO_BASE256 = 231;
|
||||
/**
|
||||
* FNC1 Codeword
|
||||
*/
|
||||
//private static final char FNC1 = 232;
|
||||
/**
|
||||
* Structured Append Codeword
|
||||
*/
|
||||
//private static final char STRUCTURED_APPEND = 233;
|
||||
/**
|
||||
* Reader Programming
|
||||
*/
|
||||
//private static final char READER_PROGRAMMING = 234;
|
||||
/**
|
||||
* Upper Shift
|
||||
*/
|
||||
static final char UPPER_SHIFT = 235;
|
||||
/**
|
||||
* 05 Macro
|
||||
*/
|
||||
private static final char MACRO_05 = 236;
|
||||
/**
|
||||
* 06 Macro
|
||||
*/
|
||||
private static final char MACRO_06 = 237;
|
||||
/**
|
||||
* mode latch to ANSI X.12 encodation mode
|
||||
*/
|
||||
static final char LATCH_TO_ANSIX12 = 238;
|
||||
/**
|
||||
* mode latch to Text encodation mode
|
||||
*/
|
||||
static final char LATCH_TO_TEXT = 239;
|
||||
/**
|
||||
* mode latch to EDIFACT encodation mode
|
||||
*/
|
||||
static final char LATCH_TO_EDIFACT = 240;
|
||||
/**
|
||||
* ECI character (Extended Channel Interpretation)
|
||||
*/
|
||||
//private static final char ECI = 241;
|
||||
|
||||
/**
|
||||
* Unlatch from C40 encodation
|
||||
*/
|
||||
static final char C40_UNLATCH = 254;
|
||||
/**
|
||||
* Unlatch from X12 encodation
|
||||
*/
|
||||
static final char X12_UNLATCH = 254;
|
||||
|
||||
/**
|
||||
* 05 Macro header
|
||||
*/
|
||||
static final String MACRO_05_HEADER = "[)>\u001E05\u001D";
|
||||
/**
|
||||
* 06 Macro header
|
||||
*/
|
||||
static final String MACRO_06_HEADER = "[)>\u001E06\u001D";
|
||||
/**
|
||||
* Macro trailer
|
||||
*/
|
||||
static final String MACRO_TRAILER = "\u001E\u0004";
|
||||
|
||||
static final int ASCII_ENCODATION = 0;
|
||||
static final int C40_ENCODATION = 1;
|
||||
static final int TEXT_ENCODATION = 2;
|
||||
static final int X12_ENCODATION = 3;
|
||||
static final int EDIFACT_ENCODATION = 4;
|
||||
static final int BASE256_ENCODATION = 5;
|
||||
|
||||
private HighLevelEncoder() {
|
||||
}
|
||||
|
||||
private static char randomize253State(int codewordPosition) {
|
||||
int pseudoRandom = ((149 * codewordPosition) % 253) + 1;
|
||||
int tempVariable = PAD + pseudoRandom;
|
||||
return (char) (tempVariable <= 254 ? tempVariable : tempVariable - 254);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
|
||||
* of ISO/IEC 16022:2000(E).
|
||||
*
|
||||
* @param msg the message
|
||||
* @return the encoded message (the char values range from 0 to 255)
|
||||
*/
|
||||
public static String encodeHighLevel(String msg) {
|
||||
return encodeHighLevel(msg, SymbolShapeHint.FORCE_NONE, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
|
||||
* of ISO/IEC 16022:2000(E).
|
||||
*
|
||||
* @param msg the message
|
||||
* @param shape requested shape. May be {@code SymbolShapeHint.FORCE_NONE},
|
||||
* {@code SymbolShapeHint.FORCE_SQUARE} or {@code SymbolShapeHint.FORCE_RECTANGLE}.
|
||||
* @param minSize the minimum symbol size constraint or null for no constraint
|
||||
* @param maxSize the maximum symbol size constraint or null for no constraint
|
||||
* @return the encoded message (the char values range from 0 to 255)
|
||||
*/
|
||||
public static String encodeHighLevel(String msg,
|
||||
SymbolShapeHint shape,
|
||||
Dimension minSize,
|
||||
Dimension maxSize) {
|
||||
return encodeHighLevel(msg, shape, minSize, maxSize, false);
|
||||
}
|
||||
/**
|
||||
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
|
||||
* of ISO/IEC 16022:2000(E).
|
||||
*
|
||||
* @param msg the message
|
||||
* @param shape requested shape. May be {@code SymbolShapeHint.FORCE_NONE},
|
||||
* {@code SymbolShapeHint.FORCE_SQUARE} or {@code SymbolShapeHint.FORCE_RECTANGLE}.
|
||||
* @param minSize the minimum symbol size constraint or null for no constraint
|
||||
* @param maxSize the maximum symbol size constraint or null for no constraint
|
||||
* @param forceC40 enforce C40 encoding
|
||||
* @return the encoded message (the char values range from 0 to 255)
|
||||
*/
|
||||
public static String encodeHighLevel(String msg,
|
||||
SymbolShapeHint shape,
|
||||
Dimension minSize,
|
||||
Dimension maxSize,
|
||||
boolean forceC40) {
|
||||
//the codewords 0..255 are encoded as Unicode characters
|
||||
C40Encoder c40Encoder = new C40Encoder();
|
||||
Encoder[] encoders = {
|
||||
new ASCIIEncoder(), c40Encoder, new TextEncoder(),
|
||||
new X12Encoder(), new EdifactEncoder(), new Base256Encoder()
|
||||
};
|
||||
|
||||
EncoderContext context = new EncoderContext(msg);
|
||||
context.setSymbolShape(shape);
|
||||
context.setSizeConstraints(minSize, maxSize);
|
||||
|
||||
if (msg.startsWith(MACRO_05_HEADER) && msg.endsWith(MACRO_TRAILER)) {
|
||||
context.writeCodeword(MACRO_05);
|
||||
context.setSkipAtEnd(2);
|
||||
context.pos += MACRO_05_HEADER.length();
|
||||
} else if (msg.startsWith(MACRO_06_HEADER) && msg.endsWith(MACRO_TRAILER)) {
|
||||
context.writeCodeword(MACRO_06);
|
||||
context.setSkipAtEnd(2);
|
||||
context.pos += MACRO_06_HEADER.length();
|
||||
}
|
||||
|
||||
int encodingMode = ASCII_ENCODATION; //Default mode
|
||||
|
||||
if (forceC40) {
|
||||
c40Encoder.encodeMaximal(context);
|
||||
encodingMode = context.getNewEncoding();
|
||||
context.resetEncoderSignal();
|
||||
}
|
||||
|
||||
while (context.hasMoreCharacters()) {
|
||||
encoders[encodingMode].encode(context);
|
||||
if (context.getNewEncoding() >= 0) {
|
||||
encodingMode = context.getNewEncoding();
|
||||
context.resetEncoderSignal();
|
||||
}
|
||||
}
|
||||
int len = context.getCodewordCount();
|
||||
context.updateSymbolInfo();
|
||||
int capacity = context.getSymbolInfo().getDataCapacity();
|
||||
if (len < capacity &&
|
||||
encodingMode != ASCII_ENCODATION &&
|
||||
encodingMode != BASE256_ENCODATION &&
|
||||
encodingMode != EDIFACT_ENCODATION) {
|
||||
context.writeCodeword('\u00fe'); //Unlatch (254)
|
||||
}
|
||||
//Padding
|
||||
StringBuilder codewords = context.getCodewords();
|
||||
if (codewords.length() < capacity) {
|
||||
codewords.append(PAD);
|
||||
}
|
||||
while (codewords.length() < capacity) {
|
||||
codewords.append(randomize253State(codewords.length() + 1));
|
||||
}
|
||||
|
||||
return context.getCodewords().toString();
|
||||
}
|
||||
|
||||
static int lookAheadTest(CharSequence msg, int startpos, int currentMode) {
|
||||
int newMode = lookAheadTestIntern(msg, startpos, currentMode);
|
||||
if (currentMode == X12_ENCODATION && newMode == X12_ENCODATION) {
|
||||
int endpos = Math.min(startpos + 3, msg.length());
|
||||
for (int i = startpos; i < endpos; i++) {
|
||||
if (!isNativeX12(msg.charAt(i))) {
|
||||
return ASCII_ENCODATION;
|
||||
}
|
||||
}
|
||||
} else if (currentMode == EDIFACT_ENCODATION && newMode == EDIFACT_ENCODATION) {
|
||||
int endpos = Math.min(startpos + 4, msg.length());
|
||||
for (int i = startpos; i < endpos; i++) {
|
||||
if (!isNativeEDIFACT(msg.charAt(i))) {
|
||||
return ASCII_ENCODATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
return newMode;
|
||||
}
|
||||
|
||||
static int lookAheadTestIntern(CharSequence msg, int startpos, int currentMode) {
|
||||
if (startpos >= msg.length()) {
|
||||
return currentMode;
|
||||
}
|
||||
float[] charCounts;
|
||||
//step J
|
||||
if (currentMode == ASCII_ENCODATION) {
|
||||
charCounts = new float[]{0, 1, 1, 1, 1, 1.25f};
|
||||
} else {
|
||||
charCounts = new float[]{1, 2, 2, 2, 2, 2.25f};
|
||||
charCounts[currentMode] = 0;
|
||||
}
|
||||
|
||||
int charsProcessed = 0;
|
||||
byte[] mins = new byte[6];
|
||||
int[] intCharCounts = new int[6];
|
||||
while (true) {
|
||||
//step K
|
||||
if ((startpos + charsProcessed) == msg.length()) {
|
||||
Arrays.fill(mins, (byte) 0);
|
||||
Arrays.fill(intCharCounts, 0);
|
||||
int min = findMinimums(charCounts, intCharCounts, Integer.MAX_VALUE, mins);
|
||||
int minCount = getMinimumCount(mins);
|
||||
|
||||
if (intCharCounts[ASCII_ENCODATION] == min) {
|
||||
return ASCII_ENCODATION;
|
||||
}
|
||||
if (minCount == 1) {
|
||||
if (mins[BASE256_ENCODATION] > 0) {
|
||||
return BASE256_ENCODATION;
|
||||
}
|
||||
if (mins[EDIFACT_ENCODATION] > 0) {
|
||||
return EDIFACT_ENCODATION;
|
||||
}
|
||||
if (mins[TEXT_ENCODATION] > 0) {
|
||||
return TEXT_ENCODATION;
|
||||
}
|
||||
if (mins[X12_ENCODATION] > 0) {
|
||||
return X12_ENCODATION;
|
||||
}
|
||||
}
|
||||
return C40_ENCODATION;
|
||||
}
|
||||
|
||||
char c = msg.charAt(startpos + charsProcessed);
|
||||
charsProcessed++;
|
||||
|
||||
//step L
|
||||
if (isDigit(c)) {
|
||||
charCounts[ASCII_ENCODATION] += 0.5f;
|
||||
} else if (isExtendedASCII(c)) {
|
||||
charCounts[ASCII_ENCODATION] = (float) Math.ceil(charCounts[ASCII_ENCODATION]);
|
||||
charCounts[ASCII_ENCODATION] += 2.0f;
|
||||
} else {
|
||||
charCounts[ASCII_ENCODATION] = (float) Math.ceil(charCounts[ASCII_ENCODATION]);
|
||||
charCounts[ASCII_ENCODATION]++;
|
||||
}
|
||||
|
||||
//step M
|
||||
if (isNativeC40(c)) {
|
||||
charCounts[C40_ENCODATION] += 2.0f / 3.0f;
|
||||
} else if (isExtendedASCII(c)) {
|
||||
charCounts[C40_ENCODATION] += 8.0f / 3.0f;
|
||||
} else {
|
||||
charCounts[C40_ENCODATION] += 4.0f / 3.0f;
|
||||
}
|
||||
|
||||
//step N
|
||||
if (isNativeText(c)) {
|
||||
charCounts[TEXT_ENCODATION] += 2.0f / 3.0f;
|
||||
} else if (isExtendedASCII(c)) {
|
||||
charCounts[TEXT_ENCODATION] += 8.0f / 3.0f;
|
||||
} else {
|
||||
charCounts[TEXT_ENCODATION] += 4.0f / 3.0f;
|
||||
}
|
||||
|
||||
//step O
|
||||
if (isNativeX12(c)) {
|
||||
charCounts[X12_ENCODATION] += 2.0f / 3.0f;
|
||||
} else if (isExtendedASCII(c)) {
|
||||
charCounts[X12_ENCODATION] += 13.0f / 3.0f;
|
||||
} else {
|
||||
charCounts[X12_ENCODATION] += 10.0f / 3.0f;
|
||||
}
|
||||
|
||||
//step P
|
||||
if (isNativeEDIFACT(c)) {
|
||||
charCounts[EDIFACT_ENCODATION] += 3.0f / 4.0f;
|
||||
} else if (isExtendedASCII(c)) {
|
||||
charCounts[EDIFACT_ENCODATION] += 17.0f / 4.0f;
|
||||
} else {
|
||||
charCounts[EDIFACT_ENCODATION] += 13.0f / 4.0f;
|
||||
}
|
||||
|
||||
// step Q
|
||||
if (isSpecialB256(c)) {
|
||||
charCounts[BASE256_ENCODATION] += 4.0f;
|
||||
} else {
|
||||
charCounts[BASE256_ENCODATION]++;
|
||||
}
|
||||
|
||||
//step R
|
||||
if (charsProcessed >= 4) {
|
||||
Arrays.fill(mins, (byte) 0);
|
||||
Arrays.fill(intCharCounts, 0);
|
||||
findMinimums(charCounts, intCharCounts, Integer.MAX_VALUE, mins);
|
||||
|
||||
if (intCharCounts[ASCII_ENCODATION] < min(intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[C40_ENCODATION], intCharCounts[TEXT_ENCODATION], intCharCounts[X12_ENCODATION],
|
||||
intCharCounts[EDIFACT_ENCODATION])) {
|
||||
return ASCII_ENCODATION;
|
||||
}
|
||||
if (intCharCounts[BASE256_ENCODATION] < intCharCounts[ASCII_ENCODATION] ||
|
||||
intCharCounts[BASE256_ENCODATION] + 1 < min(intCharCounts[C40_ENCODATION],
|
||||
intCharCounts[TEXT_ENCODATION], intCharCounts[X12_ENCODATION], intCharCounts[EDIFACT_ENCODATION])) {
|
||||
return BASE256_ENCODATION;
|
||||
}
|
||||
if (intCharCounts[EDIFACT_ENCODATION] + 1 < min(intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[C40_ENCODATION] , intCharCounts[TEXT_ENCODATION] , intCharCounts[X12_ENCODATION],
|
||||
intCharCounts[ASCII_ENCODATION])) {
|
||||
return EDIFACT_ENCODATION;
|
||||
}
|
||||
if (intCharCounts[TEXT_ENCODATION] + 1 < min(intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[C40_ENCODATION] , intCharCounts[EDIFACT_ENCODATION] , intCharCounts[X12_ENCODATION],
|
||||
intCharCounts[ASCII_ENCODATION])) {
|
||||
return TEXT_ENCODATION;
|
||||
}
|
||||
if (intCharCounts[X12_ENCODATION] + 1 < min(intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[C40_ENCODATION] , intCharCounts[EDIFACT_ENCODATION] , intCharCounts[TEXT_ENCODATION],
|
||||
intCharCounts[ASCII_ENCODATION])) {
|
||||
return X12_ENCODATION;
|
||||
}
|
||||
if (intCharCounts[C40_ENCODATION] + 1 < min(intCharCounts[ASCII_ENCODATION],
|
||||
intCharCounts[BASE256_ENCODATION] , intCharCounts[EDIFACT_ENCODATION] , intCharCounts[TEXT_ENCODATION])) {
|
||||
if (intCharCounts[C40_ENCODATION] < intCharCounts[X12_ENCODATION]) {
|
||||
return C40_ENCODATION;
|
||||
}
|
||||
if (intCharCounts[C40_ENCODATION] == intCharCounts[X12_ENCODATION]) {
|
||||
int p = startpos + charsProcessed + 1;
|
||||
while (p < msg.length()) {
|
||||
char tc = msg.charAt(p);
|
||||
if (isX12TermSep(tc)) {
|
||||
return X12_ENCODATION;
|
||||
}
|
||||
if (!isNativeX12(tc)) {
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return C40_ENCODATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int min(int f1, int f2, int f3, int f4, int f5) {
|
||||
return Math.min(min(f1, f2, f3, f4),f5);
|
||||
}
|
||||
|
||||
private static int min(int f1, int f2, int f3, int f4) {
|
||||
return Math.min(f1, Math.min(f2, Math.min(f3, f4)));
|
||||
}
|
||||
|
||||
private static int findMinimums(float[] charCounts, int[] intCharCounts, int min, byte[] mins) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int current = (intCharCounts[i] = (int) Math.ceil(charCounts[i]));
|
||||
if (min > current) {
|
||||
min = current;
|
||||
Arrays.fill(mins, (byte) 0);
|
||||
}
|
||||
if (min == current) {
|
||||
mins[i]++;
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
private static int getMinimumCount(byte[] mins) {
|
||||
int minCount = 0;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
minCount += mins[i];
|
||||
}
|
||||
return minCount;
|
||||
}
|
||||
|
||||
static boolean isDigit(char ch) {
|
||||
return ch >= '0' && ch <= '9';
|
||||
}
|
||||
|
||||
static boolean isExtendedASCII(char ch) {
|
||||
return ch >= 128 && ch <= 255;
|
||||
}
|
||||
|
||||
static boolean isNativeC40(char ch) {
|
||||
return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');
|
||||
}
|
||||
|
||||
static boolean isNativeText(char ch) {
|
||||
return (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z');
|
||||
}
|
||||
|
||||
static boolean isNativeX12(char ch) {
|
||||
return isX12TermSep(ch) || (ch == ' ') || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z');
|
||||
}
|
||||
|
||||
private static boolean isX12TermSep(char ch) {
|
||||
return (ch == '\r') //CR
|
||||
|| (ch == '*')
|
||||
|| (ch == '>');
|
||||
}
|
||||
|
||||
static boolean isNativeEDIFACT(char ch) {
|
||||
return ch >= ' ' && ch <= '^';
|
||||
}
|
||||
|
||||
private static boolean isSpecialB256(char ch) {
|
||||
return false; //TODO NOT IMPLEMENTED YET!!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the number of consecutive characters that are encodable using numeric compaction.
|
||||
*
|
||||
* @param msg the message
|
||||
* @param startpos the start position within the message
|
||||
* @return the requested character count
|
||||
*/
|
||||
public static int determineConsecutiveDigitCount(CharSequence msg, int startpos) {
|
||||
int len = msg.length();
|
||||
int idx = startpos;
|
||||
while (idx < len && isDigit(msg.charAt(idx))) {
|
||||
idx++;
|
||||
}
|
||||
return idx - startpos;
|
||||
}
|
||||
|
||||
static void illegalCharacter(char c) {
|
||||
String hex = Integer.toHexString(c);
|
||||
hex = "0000".substring(0, 4 - hex.length()) + hex;
|
||||
throw new IllegalArgumentException("Illegal character: " + c + " (0x" + hex + ')');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 Jeremias Maerki.
|
||||
*
|
||||
* 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.datamatrix.encoder;
|
||||
|
||||
final class TextEncoder extends C40Encoder {
|
||||
|
||||
@Override
|
||||
public int getEncodingMode() {
|
||||
return HighLevelEncoder.TEXT_ENCODATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
int encodeChar(char c, StringBuilder sb) {
|
||||
if (c == ' ') {
|
||||
sb.append('\3');
|
||||
return 1;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
sb.append((char) (c - 48 + 4));
|
||||
return 1;
|
||||
}
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
sb.append((char) (c - 97 + 14));
|
||||
return 1;
|
||||
}
|
||||
if (c < ' ') {
|
||||
sb.append('\0'); //Shift 1 Set
|
||||
sb.append(c);
|
||||
return 2;
|
||||
}
|
||||
if (c <= '/') {
|
||||
sb.append('\1'); //Shift 2 Set
|
||||
sb.append((char) (c - 33));
|
||||
return 2;
|
||||
}
|
||||
if (c <= '@') {
|
||||
sb.append('\1'); //Shift 2 Set
|
||||
sb.append((char) (c - 58 + 15));
|
||||
return 2;
|
||||
}
|
||||
if (c >= '[' && c <= '_') {
|
||||
sb.append('\1'); //Shift 2 Set
|
||||
sb.append((char) (c - 91 + 22));
|
||||
return 2;
|
||||
}
|
||||
if (c == '`') {
|
||||
sb.append('\2'); //Shift 3 Set
|
||||
sb.append((char) 0); // '`' - 96 == 0
|
||||
return 2;
|
||||
}
|
||||
if (c <= 'Z') {
|
||||
sb.append('\2'); //Shift 3 Set
|
||||
sb.append((char) (c - 65 + 1));
|
||||
return 2;
|
||||
}
|
||||
if (c <= 127) {
|
||||
sb.append('\2'); //Shift 3 Set
|
||||
sb.append((char) (c - 123 + 27));
|
||||
return 2;
|
||||
}
|
||||
sb.append("\1\u001e"); //Shift 2, Upper Shift
|
||||
int len = 2;
|
||||
len += encodeChar((char) (c - 128), sb);
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
114
src/datamatrix/encoder/ascii_encoder.rs
Normal file
114
src/datamatrix/encoder/ascii_encoder.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2006-2007 Jeremias Maerki.
|
||||
*
|
||||
* 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 crate::Exceptions;
|
||||
|
||||
use super::{high_level_encoder, Encoder};
|
||||
|
||||
pub struct ASCIIEncoder;
|
||||
|
||||
impl Encoder for ASCIIEncoder {
|
||||
fn encode(&self, context: &mut super::EncoderContext) -> Result<(), Exceptions> {
|
||||
//step B
|
||||
let n =
|
||||
high_level_encoder::determineConsecutiveDigitCount(context.getMessage(), context.pos);
|
||||
if n >= 2 {
|
||||
context.writeCodeword(Self::encodeASCIIDigits(
|
||||
context
|
||||
.getMessage()
|
||||
.chars()
|
||||
.nth(context.pos as usize)
|
||||
.unwrap(),
|
||||
context
|
||||
.getMessage()
|
||||
.chars()
|
||||
.nth(context.pos as usize + 1)
|
||||
.unwrap(),
|
||||
)? as u8);
|
||||
context.pos += 2;
|
||||
} else {
|
||||
let c = context.getCurrentChar();
|
||||
let newMode = high_level_encoder::lookAheadTest(
|
||||
context.getMessage(),
|
||||
context.pos,
|
||||
self.getEncodingMode() as u32,
|
||||
);
|
||||
if newMode != self.getEncodingMode() {
|
||||
match newMode {
|
||||
high_level_encoder::BASE256_ENCODATION => {
|
||||
context.writeCodeword(high_level_encoder::LATCH_TO_BASE256);
|
||||
context.signalEncoderChange(high_level_encoder::BASE256_ENCODATION);
|
||||
return Ok(());
|
||||
}
|
||||
high_level_encoder::C40_ENCODATION => {
|
||||
context.writeCodeword(high_level_encoder::LATCH_TO_C40);
|
||||
context.signalEncoderChange(high_level_encoder::C40_ENCODATION);
|
||||
return Ok(());
|
||||
}
|
||||
high_level_encoder::X12_ENCODATION => {
|
||||
context.writeCodeword(high_level_encoder::LATCH_TO_ANSIX12);
|
||||
context.signalEncoderChange(high_level_encoder::X12_ENCODATION);
|
||||
}
|
||||
high_level_encoder::TEXT_ENCODATION => {
|
||||
context.writeCodeword(high_level_encoder::LATCH_TO_TEXT);
|
||||
context.signalEncoderChange(high_level_encoder::TEXT_ENCODATION);
|
||||
}
|
||||
|
||||
high_level_encoder::EDIFACT_ENCODATION => {
|
||||
context.writeCodeword(high_level_encoder::LATCH_TO_EDIFACT);
|
||||
context.signalEncoderChange(high_level_encoder::EDIFACT_ENCODATION);
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(Exceptions::IllegalStateException(format!(
|
||||
"Illegal mode: {}",
|
||||
newMode
|
||||
)));
|
||||
}
|
||||
}
|
||||
} else if high_level_encoder::isExtendedASCII(c) {
|
||||
context.writeCodeword(high_level_encoder::UPPER_SHIFT);
|
||||
context.writeCodeword(c as u8 - 128 + 1);
|
||||
context.pos += 1;
|
||||
} else {
|
||||
context.writeCodeword(c as u8 + 1);
|
||||
context.pos += 1;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn getEncodingMode(&self) -> usize {
|
||||
high_level_encoder::ASCII_ENCODATION
|
||||
}
|
||||
}
|
||||
|
||||
impl ASCIIEncoder {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
fn encodeASCIIDigits(digit1: char, digit2: char) -> Result<char, Exceptions> {
|
||||
if high_level_encoder::isDigit(digit1) && high_level_encoder::isDigit(digit2) {
|
||||
let num = (digit1 as u8 - 48) * 10 + (digit2 as u8 - 48);
|
||||
Ok((num + 130) as char)
|
||||
} else {
|
||||
Err(Exceptions::IllegalArgumentException(format!(
|
||||
"not digits: {}{}",
|
||||
digit1, digit2
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
274
src/datamatrix/encoder/c40_encoder.rs
Normal file
274
src/datamatrix/encoder/c40_encoder.rs
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright 2006-2007 Jeremias Maerki.
|
||||
*
|
||||
* 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 crate::Exceptions;
|
||||
|
||||
use super::high_level_encoder::{
|
||||
self, ASCII_ENCODATION, C40_ENCODATION, C40_UNLATCH, LATCH_TO_C40,
|
||||
};
|
||||
|
||||
use super::{Encoder, EncoderContext};
|
||||
|
||||
pub struct C40Encoder;
|
||||
|
||||
impl Encoder for C40Encoder {
|
||||
fn encode(&self, context: &mut super::EncoderContext) -> Result<(), Exceptions> {
|
||||
self.encode_with_encode_char_fn(context, &Self::encodeChar_c40)
|
||||
}
|
||||
|
||||
fn getEncodingMode(&self) -> usize {
|
||||
C40_ENCODATION
|
||||
}
|
||||
}
|
||||
|
||||
impl C40Encoder {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub fn encode_with_encode_char_fn(
|
||||
&self,
|
||||
context: &mut super::EncoderContext,
|
||||
encodeChar: &dyn Fn(char, &mut String) -> u32,
|
||||
) -> Result<(), Exceptions> {
|
||||
//step C
|
||||
let mut buffer = String::new();
|
||||
while context.hasMoreCharacters() {
|
||||
let c = context.getCurrentChar();
|
||||
context.pos += 1;
|
||||
|
||||
let lastCharSize = encodeChar(c, &mut buffer);
|
||||
|
||||
let unwritten = (buffer.len() / 3) * 2;
|
||||
|
||||
let curCodewordCount = context.getCodewordCount() + unwritten;
|
||||
context.updateSymbolInfoWithLength(curCodewordCount);
|
||||
let available =
|
||||
context.getSymbolInfo().unwrap().getDataCapacity() as usize - curCodewordCount;
|
||||
|
||||
if !context.hasMoreCharacters() {
|
||||
//Avoid having a single C40 value in the last triplet
|
||||
let mut removed = String::new();
|
||||
if (buffer.len() % 3) == 2 && available != 2 {
|
||||
lastCharSize = self.backtrackOneCharacter(
|
||||
context,
|
||||
&mut buffer,
|
||||
&mut removed,
|
||||
lastCharSize,
|
||||
encodeChar,
|
||||
);
|
||||
}
|
||||
while (buffer.len() % 3) == 1 && (lastCharSize > 3 || available != 1) {
|
||||
lastCharSize = self.backtrackOneCharacter(
|
||||
context,
|
||||
&mut buffer,
|
||||
&removed,
|
||||
lastCharSize,
|
||||
encodeChar,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
let count = buffer.len();
|
||||
if (count % 3) == 0 {
|
||||
let newMode = high_level_encoder::lookAheadTest(
|
||||
context.getMessage(),
|
||||
context.pos,
|
||||
self.getEncodingMode() as u32,
|
||||
);
|
||||
if newMode != self.getEncodingMode() {
|
||||
// Return to ASCII encodation, which will actually handle latch to new mode
|
||||
context.signalEncoderChange(ASCII_ENCODATION);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.handleEOD(context, &mut buffer);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encodeMaximal(
|
||||
&self,
|
||||
context: &EncoderContext,
|
||||
encodeChar: &dyn Fn(char, &mut String) -> u32,
|
||||
) {
|
||||
let buffer = String::new();
|
||||
let lastCharSize = 0;
|
||||
let backtrackStartPosition = context.pos;
|
||||
let backtrackBufferLength = 0;
|
||||
while context.hasMoreCharacters() {
|
||||
let c = context.getCurrentChar();
|
||||
context.pos += 1;
|
||||
lastCharSize = encodeChar(c, &mut buffer);
|
||||
if buffer.len() % 3 == 0 {
|
||||
backtrackStartPosition = context.pos;
|
||||
backtrackBufferLength = buffer.len();
|
||||
}
|
||||
}
|
||||
if backtrackBufferLength != buffer.len() {
|
||||
let unwritten = (buffer.len() / 3) * 2;
|
||||
|
||||
let curCodewordCount = context.getCodewordCount() + unwritten + 1; // +1 for the latch to C40
|
||||
context.updateSymbolInfoWithLength(curCodewordCount);
|
||||
let available =
|
||||
context.getSymbolInfo().unwrap().getDataCapacity() as usize - curCodewordCount;
|
||||
let rest = buffer.len() % 3;
|
||||
if (rest == 2 && available != 2) || (rest == 1 && (lastCharSize > 3 || available != 1))
|
||||
{
|
||||
buffer.truncate(backtrackBufferLength);
|
||||
// buffer.setLength(backtrackBufferLength);
|
||||
context.pos = backtrackStartPosition;
|
||||
}
|
||||
}
|
||||
if buffer.len() > 0 {
|
||||
context.writeCodeword(LATCH_TO_C40);
|
||||
}
|
||||
|
||||
self.handleEOD(context, &mut buffer);
|
||||
}
|
||||
|
||||
fn backtrackOneCharacter(
|
||||
&self,
|
||||
context: &EncoderContext,
|
||||
buffer: &mut String,
|
||||
removed: &String,
|
||||
lastCharSize: u32,
|
||||
encodeChar: &dyn Fn(char, &mut String) -> u32,
|
||||
) -> u32 {
|
||||
let count = buffer.len();
|
||||
// buffer.delete(count - lastCharSize, count);
|
||||
buffer.replace_range((count - lastCharSize as usize)..count, "");
|
||||
context.pos -= 1;
|
||||
let c = context.getCurrentChar();
|
||||
lastCharSize = encodeChar(c, &mut removed);
|
||||
context.resetSymbolInfo(); //Deal with possible reduction in symbol size
|
||||
return lastCharSize;
|
||||
}
|
||||
|
||||
fn writeNextTriplet(context: &EncoderContext, buffer: &mut String) {
|
||||
context.writeCodewords(&Self::encodeToCodewords(buffer));
|
||||
buffer.replace_range(0..3, "");
|
||||
// buffer.delete(0, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle "end of data" situations
|
||||
*
|
||||
* @param context the encoder context
|
||||
* @param buffer the buffer with the remaining encoded characters
|
||||
*/
|
||||
fn handleEOD(&self, context: &EncoderContext, buffer: &mut String) -> Result<(), Exceptions> {
|
||||
let unwritten = (buffer.len() / 3) * 2;
|
||||
let rest = buffer.len() % 3;
|
||||
|
||||
let curCodewordCount = context.getCodewordCount() + unwritten;
|
||||
context.updateSymbolInfoWithLength(curCodewordCount);
|
||||
let available =
|
||||
context.getSymbolInfo().unwrap().getDataCapacity() as usize - curCodewordCount;
|
||||
|
||||
if rest == 2 {
|
||||
buffer.push('\0'); //Shift 1
|
||||
while buffer.len() >= 3 {
|
||||
C40Encoder::writeNextTriplet(context, buffer);
|
||||
}
|
||||
if context.hasMoreCharacters() {
|
||||
context.writeCodeword(C40_UNLATCH);
|
||||
}
|
||||
} else if available == 1 && rest == 1 {
|
||||
while buffer.len() >= 3 {
|
||||
C40Encoder::writeNextTriplet(context, buffer);
|
||||
}
|
||||
if context.hasMoreCharacters() {
|
||||
context.writeCodeword(C40_UNLATCH);
|
||||
}
|
||||
// else no unlatch
|
||||
context.pos -= 1;
|
||||
} else if rest == 0 {
|
||||
while buffer.len() >= 3 {
|
||||
C40Encoder::writeNextTriplet(context, buffer);
|
||||
}
|
||||
if available > 0 || context.hasMoreCharacters() {
|
||||
context.writeCodeword(C40_UNLATCH);
|
||||
}
|
||||
} else {
|
||||
return Err(Exceptions::IllegalStateException(
|
||||
"Unexpected case. Please report!".to_owned(),
|
||||
));
|
||||
}
|
||||
context.signalEncoderChange(ASCII_ENCODATION);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encodeChar_c40(c: char, sb: &mut String) -> u32 {
|
||||
if c == ' ' {
|
||||
sb.push('\u{3}');
|
||||
return 1;
|
||||
}
|
||||
if c >= '0' && c <= '9' {
|
||||
sb.push((c as u8 - 48 + 4) as char);
|
||||
return 1;
|
||||
}
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
sb.push((c as u8 - 65 + 14) as char);
|
||||
return 1;
|
||||
}
|
||||
if c < ' ' {
|
||||
sb.push('\0'); //Shift 1 Set
|
||||
sb.push(c);
|
||||
return 2;
|
||||
}
|
||||
if c <= '/' {
|
||||
sb.push('\u{1}'); //Shift 2 Set
|
||||
sb.push((c as u8 - 33) as char);
|
||||
return 2;
|
||||
}
|
||||
if c <= '@' {
|
||||
sb.push('\u{1}'); //Shift 2 Set
|
||||
sb.push((c as u8 - 58 + 15) as char);
|
||||
return 2;
|
||||
}
|
||||
if c <= '_' {
|
||||
sb.push('\u{1}'); //Shift 2 Set
|
||||
sb.push((c as u8 - 91 + 22) as char);
|
||||
return 2;
|
||||
}
|
||||
if (c as u8) <= 127 {
|
||||
sb.push('\u{2}'); //Shift 3 Set
|
||||
sb.push((c as u8 - 96) as char);
|
||||
return 2;
|
||||
}
|
||||
sb.push_str("\u{1}\u{001e}"); //Shift 2, Upper Shift
|
||||
let len = 2;
|
||||
len += Self::encodeChar_c40((c as u8 - 128) as char, sb);
|
||||
|
||||
len
|
||||
}
|
||||
|
||||
fn encodeToCodewords(sb: &str) -> String {
|
||||
let v = (1600 * sb.chars().nth(0).unwrap() as u32)
|
||||
+ (40 * sb.chars().nth(1).unwrap() as u32)
|
||||
+ sb.chars().nth(2).unwrap() as u32
|
||||
+ 1;
|
||||
let cw1 = v / 256;
|
||||
let cw2 = v % 256;
|
||||
[char::from_u32(cw1).unwrap(), char::from_u32(cw2).unwrap()]
|
||||
.into_iter()
|
||||
.collect()
|
||||
// return new String(new char[] {cw1, cw2});
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::Exceptions;
|
||||
|
||||
use super::EncoderContext;
|
||||
|
||||
pub trait Encoder {
|
||||
fn getEncodingMode(&self) -> usize;
|
||||
|
||||
fn getEncodingMode(&self) -> i32;
|
||||
|
||||
fn encode(&self, context:&mut EncoderContext);
|
||||
|
||||
fn encode(&self, context: &mut EncoderContext) -> Result<(), Exceptions>;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ pub struct EncoderContext<'a> {
|
||||
minSize: Option<Dimension>,
|
||||
maxSize: Option<Dimension>,
|
||||
codewords: String,
|
||||
pos: u32,
|
||||
newEncoding: i32,
|
||||
pub(super) pos: u32,
|
||||
newEncoding:Option< usize>,
|
||||
symbolInfo: Option<&'a SymbolInfo>,
|
||||
skipAtEnd: u32,
|
||||
}
|
||||
@@ -65,7 +65,7 @@ impl EncoderContext<'_> {
|
||||
msg: sb,
|
||||
shape: SymbolShapeHint::FORCE_NONE,
|
||||
codewords: String::with_capacity(msg.len()),
|
||||
newEncoding: -1,
|
||||
newEncoding: None,
|
||||
minSize: None,
|
||||
maxSize: None,
|
||||
pos: 0,
|
||||
@@ -91,12 +91,14 @@ impl EncoderContext<'_> {
|
||||
self.skipAtEnd = count;
|
||||
}
|
||||
|
||||
pub fn getCurrentChar(&self) -> &str {
|
||||
self.msg.graphemes(true).nth(self.pos as usize).unwrap()
|
||||
pub fn getCurrentChar(&self) -> char {
|
||||
// self.msg.graphemes(true).nth(self.pos as usize).unwrap()
|
||||
self.msg.chars().nth(self.pos as usize).unwrap()
|
||||
}
|
||||
|
||||
pub fn getCurrent(&self) -> &str {
|
||||
self.msg.graphemes(true).nth(self.pos as usize).unwrap()
|
||||
pub fn getCurrent(&self) -> char {
|
||||
// self.msg.graphemes(true).nth(self.pos as usize).unwrap()
|
||||
self.msg.chars().nth(self.pos as usize).unwrap()
|
||||
}
|
||||
|
||||
pub fn getCodewords(&self) -> &str {
|
||||
@@ -107,24 +109,24 @@ impl EncoderContext<'_> {
|
||||
self.codewords.push_str(codewords);
|
||||
}
|
||||
|
||||
pub fn writeCodeword(&mut self, codeword: &str) {
|
||||
self.codewords.push_str(codeword);
|
||||
pub fn writeCodeword(&mut self, codeword: u8) {
|
||||
self.codewords.push(codeword as char);
|
||||
}
|
||||
|
||||
pub fn getCodewordCount(&self) -> usize {
|
||||
self.codewords.len()
|
||||
}
|
||||
|
||||
pub fn getNewEncoding(&self) -> i32 {
|
||||
pub fn getNewEncoding(&self) -> Option<usize> {
|
||||
self.newEncoding
|
||||
}
|
||||
|
||||
pub fn signalEncoderChange(&mut self, encoding: i32) {
|
||||
self.newEncoding = encoding;
|
||||
pub fn signalEncoderChange(&mut self, encoding: usize) {
|
||||
self.newEncoding = Some(encoding);
|
||||
}
|
||||
|
||||
pub fn resetEncoderSignal(&mut self) {
|
||||
self.newEncoding = -1;
|
||||
self.newEncoding = None;
|
||||
}
|
||||
|
||||
pub fn hasMoreCharacters(&self) -> bool {
|
||||
|
||||
576
src/datamatrix/encoder/high_level_encoder.rs
Normal file
576
src/datamatrix/encoder/high_level_encoder.rs
Normal file
@@ -0,0 +1,576 @@
|
||||
/*
|
||||
* Copyright 2006-2007 Jeremias Maerki.
|
||||
*
|
||||
* 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::rc::Rc;
|
||||
|
||||
use encoding::{self, EncodingRef};
|
||||
|
||||
use crate::{Dimension, Exceptions};
|
||||
|
||||
use super::{EncoderContext, SymbolShapeHint, C40Encoder, ASCIIEncoder, Encoder, TextEncoder};
|
||||
const DEFAULT_ENCODING: EncodingRef = encoding::all::ISO_8859_1;
|
||||
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
/**
|
||||
* DataMatrix ECC 200 data encoder following the algorithm described in ISO/IEC 16022:200(E) in
|
||||
* annex S.
|
||||
*/
|
||||
/**
|
||||
* Padding character
|
||||
*/
|
||||
const PAD: u8 = 129;
|
||||
/**
|
||||
* mode latch to C40 encodation mode
|
||||
*/
|
||||
pub const LATCH_TO_C40: u8 = 230;
|
||||
/**
|
||||
* mode latch to Base 256 encodation mode
|
||||
*/
|
||||
pub const LATCH_TO_BASE256: u8 = 231;
|
||||
/**
|
||||
* FNC1 Codeword
|
||||
*/
|
||||
//private static final char FNC1 = 232;
|
||||
/**
|
||||
* Structured Append Codeword
|
||||
*/
|
||||
//private static final char STRUCTURED_APPEND = 233;
|
||||
/**
|
||||
* Reader Programming
|
||||
*/
|
||||
//private static final char READER_PROGRAMMING = 234;
|
||||
/**
|
||||
* Upper Shift
|
||||
*/
|
||||
pub const UPPER_SHIFT: u8 = 235;
|
||||
/**
|
||||
* 05 Macro
|
||||
*/
|
||||
const MACRO_05: u8 = 236;
|
||||
/**
|
||||
* 06 Macro
|
||||
*/
|
||||
const MACRO_06: u8 = 237;
|
||||
/**
|
||||
* mode latch to ANSI X.12 encodation mode
|
||||
*/
|
||||
pub const LATCH_TO_ANSIX12: u8 = 238;
|
||||
/**
|
||||
* mode latch to Text encodation mode
|
||||
*/
|
||||
pub const LATCH_TO_TEXT: u8 = 239;
|
||||
/**
|
||||
* mode latch to EDIFACT encodation mode
|
||||
*/
|
||||
pub const LATCH_TO_EDIFACT: u8 = 240;
|
||||
/**
|
||||
* ECI character (Extended Channel Interpretation)
|
||||
*/
|
||||
//private static final char ECI = 241;
|
||||
|
||||
/**
|
||||
* Unlatch from C40 encodation
|
||||
*/
|
||||
pub const C40_UNLATCH: u8 = 254;
|
||||
/**
|
||||
* Unlatch from X12 encodation
|
||||
*/
|
||||
pub const X12_UNLATCH: u8 = 254;
|
||||
|
||||
/**
|
||||
* 05 Macro header
|
||||
*/
|
||||
pub const MACRO_05_HEADER: &str = "[)>\u{001E}05\u{001D}"; // THIS MIGHT BE WRONG, CHECK IF IT SHOULD BE a long unicode
|
||||
/**
|
||||
* 06 Macro header
|
||||
*/
|
||||
pub const MACRO_06_HEADER: &str = "[)>\u{001E}06\u{001D}"; // THIS MIGHT BE WRONG, CHECK IF IT SHOULD BE a long unicode
|
||||
/**
|
||||
* Macro trailer
|
||||
*/
|
||||
pub const MACRO_TRAILER: &str = "\u{001E}\u{0004}";
|
||||
|
||||
pub const ASCII_ENCODATION: usize = 0;
|
||||
pub const C40_ENCODATION: usize = 1;
|
||||
pub const TEXT_ENCODATION: usize = 2;
|
||||
pub const X12_ENCODATION: usize = 3;
|
||||
pub const EDIFACT_ENCODATION: usize = 4;
|
||||
pub const BASE256_ENCODATION: usize = 5;
|
||||
|
||||
fn randomize253State(codewordPosition: u32) -> String {
|
||||
let pseudoRandom = ((149 * codewordPosition) % 253) + 1;
|
||||
let tempVariable = PAD as u32 + pseudoRandom;
|
||||
if tempVariable <= 254 {
|
||||
char::from_u32(tempVariable)
|
||||
} else {
|
||||
char::from_u32(tempVariable - 254)
|
||||
}
|
||||
.expect("must become a char")
|
||||
.to_string()
|
||||
// return (char) (tempVariable <= 254 ? tempVariable : tempVariable - 254);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
|
||||
* of ISO/IEC 16022:2000(E).
|
||||
*
|
||||
* @param msg the message
|
||||
* @return the encoded message (the char values range from 0 to 255)
|
||||
*/
|
||||
pub fn encodeHighLevel(msg: &str) -> Result<String, Exceptions> {
|
||||
encodeHighLevelWithDimensionForceC40(msg, SymbolShapeHint::FORCE_NONE, None, None, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
|
||||
* of ISO/IEC 16022:2000(E).
|
||||
*
|
||||
* @param msg the message
|
||||
* @param shape requested shape. May be {@code SymbolShapeHint.FORCE_NONE},
|
||||
* {@code SymbolShapeHint.FORCE_SQUARE} or {@code SymbolShapeHint.FORCE_RECTANGLE}.
|
||||
* @param minSize the minimum symbol size constraint or null for no constraint
|
||||
* @param maxSize the maximum symbol size constraint or null for no constraint
|
||||
* @return the encoded message (the char values range from 0 to 255)
|
||||
*/
|
||||
pub fn encodeHighLevelWithDimension(
|
||||
msg: &str,
|
||||
shape: SymbolShapeHint,
|
||||
minSize: Option<Dimension>,
|
||||
maxSize: Option<Dimension>,
|
||||
) -> Result<String, Exceptions> {
|
||||
encodeHighLevelWithDimensionForceC40(msg, shape, minSize, maxSize, false)
|
||||
}
|
||||
/**
|
||||
* Performs message encoding of a DataMatrix message using the algorithm described in annex P
|
||||
* of ISO/IEC 16022:2000(E).
|
||||
*
|
||||
* @param msg the message
|
||||
* @param shape requested shape. May be {@code SymbolShapeHint.FORCE_NONE},
|
||||
* {@code SymbolShapeHint.FORCE_SQUARE} or {@code SymbolShapeHint.FORCE_RECTANGLE}.
|
||||
* @param minSize the minimum symbol size constraint or null for no constraint
|
||||
* @param maxSize the maximum symbol size constraint or null for no constraint
|
||||
* @param forceC40 enforce C40 encoding
|
||||
* @return the encoded message (the char values range from 0 to 255)
|
||||
*/
|
||||
pub fn encodeHighLevelWithDimensionForceC40(
|
||||
msg: &str,
|
||||
shape: SymbolShapeHint,
|
||||
minSize: Option<Dimension>,
|
||||
maxSize: Option<Dimension>,
|
||||
forceC40: bool,
|
||||
) -> Result<String, Exceptions> {
|
||||
//the codewords 0..255 are encoded as Unicode characters
|
||||
let c40Encoder = Rc::new(C40Encoder::new());
|
||||
let encoders:[Rc<dyn Encoder>;6] = [
|
||||
Rc::new(ASCIIEncoder::new()),
|
||||
c40Encoder.clone(),
|
||||
Rc::new(TextEncoder::new()),
|
||||
X12Encoder::new(),
|
||||
EdifactEncoder::new(),
|
||||
Base256Encoder::new(),
|
||||
];
|
||||
|
||||
let context = EncoderContext::new(msg)?;
|
||||
context.setSymbolShape(shape);
|
||||
context.setSizeConstraints(minSize, maxSize);
|
||||
|
||||
if msg.starts_with(MACRO_05_HEADER) && msg.ends_with(MACRO_TRAILER) {
|
||||
context.writeCodeword(MACRO_05);
|
||||
context.setSkipAtEnd(2);
|
||||
context.pos += MACRO_05_HEADER.len();
|
||||
} else if (msg.starts_with(MACRO_06_HEADER) && msg.ends_with(MACRO_TRAILER)) {
|
||||
context.writeCodeword(MACRO_06);
|
||||
context.setSkipAtEnd(2);
|
||||
context.pos += MACRO_06_HEADER.len();
|
||||
}
|
||||
|
||||
let encodingMode = ASCII_ENCODATION; //Default mode
|
||||
|
||||
if forceC40 {
|
||||
c40Encoder.encodeMaximal(context);
|
||||
encodingMode = context.getNewEncoding();
|
||||
context.resetEncoderSignal();
|
||||
}
|
||||
|
||||
while context.hasMoreCharacters() {
|
||||
encoders[encodingMode].encode(context);
|
||||
if context.getNewEncoding() >= 0 {
|
||||
encodingMode = context.getNewEncoding();
|
||||
context.resetEncoderSignal();
|
||||
}
|
||||
}
|
||||
let len = context.getCodewordCount();
|
||||
context.updateSymbolInfo();
|
||||
let capacity = context.getSymbolInfo().getDataCapacity();
|
||||
if len < capacity
|
||||
&& encodingMode != ASCII_ENCODATION
|
||||
&& encodingMode != BASE256_ENCODATION
|
||||
&& encodingMode != EDIFACT_ENCODATION
|
||||
{
|
||||
context.writeCodeword("\u{00fe}"); //Unlatch (254)
|
||||
}
|
||||
//Padding
|
||||
let codewords = context.getCodewords();
|
||||
if codewords.len() < capacity {
|
||||
codewords.append(PAD);
|
||||
}
|
||||
while codewords.len() < capacity {
|
||||
codewords.append(randomize253State(codewords.length() + 1));
|
||||
}
|
||||
|
||||
return context.getCodewords().toString();
|
||||
}
|
||||
|
||||
pub fn lookAheadTest(msg: &str, startpos: u32, currentMode: u32) -> usize {
|
||||
let newMode = lookAheadTestIntern(msg, startpos, currentMode);
|
||||
if currentMode as usize == X12_ENCODATION && newMode as usize == X12_ENCODATION {
|
||||
let msg_graphemes = msg.graphemes(true);
|
||||
let endpos = (startpos + 3).min(msg_graphemes.count() as u32);
|
||||
for i in startpos..endpos {
|
||||
// for (int i = startpos; i < endpos; i++) {
|
||||
if !isNativeX12(msg_graphemes.nth(i as usize).unwrap()) {
|
||||
return ASCII_ENCODATION;
|
||||
}
|
||||
}
|
||||
} else if currentMode as usize == EDIFACT_ENCODATION && newMode == EDIFACT_ENCODATION {
|
||||
let msg_graphemes = msg.graphemes(true);
|
||||
let endpos = (startpos + 4).min(msg_graphemes.count() as u32);
|
||||
for i in startpos..endpos {
|
||||
// for (int i = startpos; i < endpos; i++) {
|
||||
if !isNativeEDIFACT(msg_graphemes.nth(i as usize).unwrap()) {
|
||||
return ASCII_ENCODATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
newMode
|
||||
}
|
||||
|
||||
fn lookAheadTestIntern(msg: &str, startpos: u32, currentMode: u32) -> usize {
|
||||
if startpos as usize >= msg.len() {
|
||||
return currentMode as usize;
|
||||
}
|
||||
let charCounts: [f32; 6];
|
||||
//step J
|
||||
if currentMode == ASCII_ENCODATION as u32 {
|
||||
charCounts = [0.0, 1.0, 1.0, 1.0, 1.0, 1.25];
|
||||
} else {
|
||||
charCounts = [1.0, 2.0, 2.0, 2.0, 2.0, 2.25];
|
||||
charCounts[currentMode as usize] = 0.0;
|
||||
}
|
||||
|
||||
let charsProcessed = 0;
|
||||
let mins = [0u8; 6];
|
||||
let intCharCounts = [0u32; 6];
|
||||
loop {
|
||||
//step K
|
||||
if (startpos + charsProcessed) == msg.len() as u32 {
|
||||
mins.fill(0);
|
||||
intCharCounts.fill(0);
|
||||
// Arrays.fill(mins, (byte) 0);
|
||||
// Arrays.fill(intCharCounts, 0);
|
||||
let min = findMinimums(&charCounts, &intCharCounts, u32::MAX, &mins);
|
||||
let minCount = getMinimumCount(&mins);
|
||||
|
||||
if intCharCounts[ASCII_ENCODATION] == min {
|
||||
return ASCII_ENCODATION;
|
||||
}
|
||||
if minCount == 1 {
|
||||
if mins[BASE256_ENCODATION] > 0 {
|
||||
return BASE256_ENCODATION;
|
||||
}
|
||||
if mins[EDIFACT_ENCODATION] > 0 {
|
||||
return EDIFACT_ENCODATION;
|
||||
}
|
||||
if mins[TEXT_ENCODATION] > 0 {
|
||||
return TEXT_ENCODATION;
|
||||
}
|
||||
if mins[X12_ENCODATION] > 0 {
|
||||
return X12_ENCODATION;
|
||||
}
|
||||
}
|
||||
return C40_ENCODATION;
|
||||
}
|
||||
|
||||
let c = msg
|
||||
.graphemes(true)
|
||||
.nth((startpos + charsProcessed) as usize)
|
||||
.unwrap();
|
||||
// let c = msg.charAt(startpos + charsProcessed);
|
||||
charsProcessed += 1;
|
||||
|
||||
//step L
|
||||
if isDigit(c) {
|
||||
charCounts[ASCII_ENCODATION] += 0.5;
|
||||
} else if isExtendedASCII(c) {
|
||||
charCounts[ASCII_ENCODATION] = charCounts[ASCII_ENCODATION].ceil();
|
||||
charCounts[ASCII_ENCODATION] += 2.0;
|
||||
} else {
|
||||
charCounts[ASCII_ENCODATION] = charCounts[ASCII_ENCODATION].ceil();
|
||||
charCounts[ASCII_ENCODATION] += 1.0;
|
||||
}
|
||||
|
||||
//step M
|
||||
if isNativeC40(c) {
|
||||
charCounts[C40_ENCODATION] += 2.0 / 3.0;
|
||||
} else if isExtendedASCII(c) {
|
||||
charCounts[C40_ENCODATION] += 8.0 / 3.0;
|
||||
} else {
|
||||
charCounts[C40_ENCODATION] += 4.0 / 3.0;
|
||||
}
|
||||
|
||||
//step N
|
||||
if isNativeText(c) {
|
||||
charCounts[TEXT_ENCODATION] += 2.0 / 3.0;
|
||||
} else if isExtendedASCII(c) {
|
||||
charCounts[TEXT_ENCODATION] += 8.0 / 3.0;
|
||||
} else {
|
||||
charCounts[TEXT_ENCODATION] += 4.0 / 3.0;
|
||||
}
|
||||
|
||||
//step O
|
||||
if isNativeX12(c) {
|
||||
charCounts[X12_ENCODATION] += 2.0 / 3.0;
|
||||
} else if isExtendedASCII(c) {
|
||||
charCounts[X12_ENCODATION] += 13.0 / 3.0;
|
||||
} else {
|
||||
charCounts[X12_ENCODATION] += 10.0 / 3.0;
|
||||
}
|
||||
|
||||
//step P
|
||||
if isNativeEDIFACT(c) {
|
||||
charCounts[EDIFACT_ENCODATION] += 3.0 / 4.0;
|
||||
} else if isExtendedASCII(c) {
|
||||
charCounts[EDIFACT_ENCODATION] += 17.0 / 4.0;
|
||||
} else {
|
||||
charCounts[EDIFACT_ENCODATION] += 13.0 / 4.0;
|
||||
}
|
||||
|
||||
// step Q
|
||||
if isSpecialB256(c) {
|
||||
charCounts[BASE256_ENCODATION] += 4.0;
|
||||
} else {
|
||||
charCounts[BASE256_ENCODATION] += 1.0;
|
||||
}
|
||||
|
||||
//step R
|
||||
if charsProcessed >= 4 {
|
||||
mins.fill(0);
|
||||
intCharCounts.fill(0);
|
||||
// Arrays.fill(mins, (byte) 0);
|
||||
// Arrays.fill(intCharCounts, 0);
|
||||
findMinimums(&charCounts, &intCharCounts, u32::MAX, &mins);
|
||||
|
||||
if intCharCounts[ASCII_ENCODATION]
|
||||
< min5(
|
||||
intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[C40_ENCODATION],
|
||||
intCharCounts[TEXT_ENCODATION],
|
||||
intCharCounts[X12_ENCODATION],
|
||||
intCharCounts[EDIFACT_ENCODATION],
|
||||
)
|
||||
{
|
||||
return ASCII_ENCODATION;
|
||||
}
|
||||
if intCharCounts[BASE256_ENCODATION] < intCharCounts[ASCII_ENCODATION]
|
||||
|| intCharCounts[BASE256_ENCODATION] + 1
|
||||
< min4(
|
||||
intCharCounts[C40_ENCODATION],
|
||||
intCharCounts[TEXT_ENCODATION],
|
||||
intCharCounts[X12_ENCODATION],
|
||||
intCharCounts[EDIFACT_ENCODATION],
|
||||
)
|
||||
{
|
||||
return BASE256_ENCODATION;
|
||||
}
|
||||
if intCharCounts[EDIFACT_ENCODATION] + 1
|
||||
< min5(
|
||||
intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[C40_ENCODATION],
|
||||
intCharCounts[TEXT_ENCODATION],
|
||||
intCharCounts[X12_ENCODATION],
|
||||
intCharCounts[ASCII_ENCODATION],
|
||||
)
|
||||
{
|
||||
return EDIFACT_ENCODATION;
|
||||
}
|
||||
if intCharCounts[TEXT_ENCODATION] + 1
|
||||
< min5(
|
||||
intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[C40_ENCODATION],
|
||||
intCharCounts[EDIFACT_ENCODATION],
|
||||
intCharCounts[X12_ENCODATION],
|
||||
intCharCounts[ASCII_ENCODATION],
|
||||
)
|
||||
{
|
||||
return TEXT_ENCODATION;
|
||||
}
|
||||
if intCharCounts[X12_ENCODATION] + 1
|
||||
< min5(
|
||||
intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[C40_ENCODATION],
|
||||
intCharCounts[EDIFACT_ENCODATION],
|
||||
intCharCounts[TEXT_ENCODATION],
|
||||
intCharCounts[ASCII_ENCODATION],
|
||||
)
|
||||
{
|
||||
return X12_ENCODATION;
|
||||
}
|
||||
if intCharCounts[C40_ENCODATION] + 1
|
||||
< min4(
|
||||
intCharCounts[ASCII_ENCODATION],
|
||||
intCharCounts[BASE256_ENCODATION],
|
||||
intCharCounts[EDIFACT_ENCODATION],
|
||||
intCharCounts[TEXT_ENCODATION],
|
||||
)
|
||||
{
|
||||
if intCharCounts[C40_ENCODATION] < intCharCounts[X12_ENCODATION] {
|
||||
return C40_ENCODATION;
|
||||
}
|
||||
if intCharCounts[C40_ENCODATION] == intCharCounts[X12_ENCODATION] {
|
||||
let p = startpos + charsProcessed + 1;
|
||||
for tc in msg.graphemes(true) {
|
||||
// while (p as usize) < msg.len() {
|
||||
// let tc = msg.charAt(p);
|
||||
if isX12TermSep(tc) {
|
||||
return X12_ENCODATION;
|
||||
}
|
||||
if !isNativeX12(tc) {
|
||||
break;
|
||||
}
|
||||
p += 1;
|
||||
}
|
||||
return C40_ENCODATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn min5(f1: u32, f2: u32, f3: u32, f4: u32, f5: u32) -> u32 {
|
||||
min4(f1, f2, f3, f4).min(f5)
|
||||
}
|
||||
|
||||
fn min4(f1: u32, f2: u32, f3: u32, f4: u32) -> u32 {
|
||||
f1.min(f2.min(f3.min(f4)))
|
||||
// Math.min(f1, Math.min(f2, Math.min(f3, f4)))
|
||||
}
|
||||
|
||||
fn findMinimums(charCounts: &[f32; 6], intCharCounts: &[u32; 6], min: u32, mins: &[u8]) -> u32 {
|
||||
for i in 0..6 {
|
||||
// for (int i = 0; i < 6; i++) {
|
||||
intCharCounts[i] = charCounts[i].ceil() as u32;
|
||||
let current = intCharCounts[i]; // = (int) Math.ceil(charCounts[i]));
|
||||
if min > current {
|
||||
min = current;
|
||||
mins.fill(0);
|
||||
// Arrays.fill(mins, (byte) 0);
|
||||
}
|
||||
if min == current {
|
||||
mins[i] += 1;
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
fn getMinimumCount(mins: &[u8]) -> u32 {
|
||||
let minCount = 0;
|
||||
for i in 0..6 {
|
||||
// for (int i = 0; i < 6; i++) {
|
||||
minCount += mins[i] as u32;
|
||||
}
|
||||
minCount
|
||||
}
|
||||
|
||||
pub fn isDigit(ch: char) -> bool {
|
||||
ch >= '0' && ch <= '9'
|
||||
}
|
||||
|
||||
pub fn isExtendedASCII(ch: char) -> bool {
|
||||
(ch as u8) >= 128 && (ch as u8) <= 255
|
||||
}
|
||||
|
||||
fn isNativeC40(ch: &str) -> bool {
|
||||
if ch.len() > 1 {
|
||||
return false;
|
||||
}
|
||||
let cha = ch.chars().nth(0).unwrap();
|
||||
(cha == ' ') || (cha >= '0' && cha <= '9') || (cha >= 'A' && cha <= 'Z')
|
||||
}
|
||||
|
||||
fn isNativeText(ch: &str) -> bool {
|
||||
if ch.len() > 1 {
|
||||
return false;
|
||||
}
|
||||
let cha = ch.chars().nth(0).unwrap();
|
||||
(cha == ' ') || (cha >= '0' && cha <= '9') || (cha >= 'a' && cha <= 'z')
|
||||
}
|
||||
|
||||
fn isNativeX12(ch: &str) -> bool {
|
||||
if ch.len() > 1 {
|
||||
return false;
|
||||
}
|
||||
let cha = ch.chars().nth(0).unwrap();
|
||||
return isX12TermSep(ch)
|
||||
|| (cha == ' ')
|
||||
|| (cha >= '0' && cha <= '9')
|
||||
|| (cha >= 'A' && cha <= 'Z');
|
||||
}
|
||||
|
||||
fn isX12TermSep(ch: &str) -> bool {
|
||||
(ch == "\r") //CR
|
||||
|| (ch == "*")
|
||||
|| (ch == ">")
|
||||
}
|
||||
|
||||
fn isNativeEDIFACT(ch: &str) -> bool {
|
||||
if ch.len() > 1 {
|
||||
return false;
|
||||
}
|
||||
let cha = ch.chars().nth(0).unwrap();
|
||||
cha >= ' ' && cha <= '^'
|
||||
}
|
||||
|
||||
fn isSpecialB256(ch: &str) -> bool {
|
||||
unimplemented!();
|
||||
return false; //TODO NOT IMPLEMENTED YET!!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the number of consecutive characters that are encodable using numeric compaction.
|
||||
*
|
||||
* @param msg the message
|
||||
* @param startpos the start position within the message
|
||||
* @return the requested character count
|
||||
*/
|
||||
pub fn determineConsecutiveDigitCount(msg: &str, startpos: u32) -> u32 {
|
||||
// let len = msg.len();
|
||||
let idx = startpos;
|
||||
let graphemes = msg.graphemes(true);
|
||||
while (idx as usize) < graphemes.count() && isDigit(graphemes.nth(idx as usize).unwrap()) {
|
||||
idx += 1;
|
||||
}
|
||||
idx - startpos
|
||||
}
|
||||
|
||||
fn illegalCharacter(c: &str) -> Result<(), Exceptions> {
|
||||
// let hex = Integer.toHexString(c);
|
||||
// hex = "0000".substring(0, 4 - hex.length()) + hex;
|
||||
Err(Exceptions::IllegalArgumentException(format!(
|
||||
"Illegal character: {} (0x{})",
|
||||
c, c
|
||||
)))
|
||||
}
|
||||
@@ -2,8 +2,18 @@ mod encoder;
|
||||
mod encoder_context;
|
||||
mod symbol_shape_hint;
|
||||
mod symbol_info;
|
||||
pub mod high_level_encoder;
|
||||
|
||||
pub use encoder::*;
|
||||
pub use encoder_context::*;
|
||||
pub use symbol_shape_hint::*;
|
||||
pub use symbol_info::*;
|
||||
|
||||
mod c40_encoder;
|
||||
pub use c40_encoder::*;
|
||||
|
||||
mod ascii_encoder;
|
||||
pub use ascii_encoder::*;
|
||||
|
||||
mod text_encoder;
|
||||
pub use text_encoder::*;
|
||||
87
src/datamatrix/encoder/text_encoder.rs
Normal file
87
src/datamatrix/encoder/text_encoder.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2006-2007 Jeremias Maerki.
|
||||
*
|
||||
* 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 super::{high_level_encoder, C40Encoder, Encoder};
|
||||
|
||||
pub struct TextEncoder(C40Encoder);
|
||||
impl Encoder for TextEncoder {
|
||||
fn getEncodingMode(&self) -> usize {
|
||||
high_level_encoder::TEXT_ENCODATION
|
||||
}
|
||||
|
||||
fn encode(&self, context: &mut super::EncoderContext) -> Result<(), crate::Exceptions> {
|
||||
self.0
|
||||
.encode_with_encode_char_fn(context, &Self::encodeChar)
|
||||
}
|
||||
}
|
||||
impl TextEncoder {
|
||||
pub fn new() -> Self {
|
||||
Self(C40Encoder::new())
|
||||
}
|
||||
fn encodeChar(c: char, sb: &mut String) -> u32 {
|
||||
if c == ' ' {
|
||||
sb.push('\u{3}');
|
||||
return 1;
|
||||
}
|
||||
if c >= '0' && c <= '9' {
|
||||
sb.push((c as u8 - 48 + 4) as char);
|
||||
return 1;
|
||||
}
|
||||
if c >= 'a' && c <= 'z' {
|
||||
sb.push((c as u8 - 97 + 14) as char);
|
||||
return 1;
|
||||
}
|
||||
if c < ' ' {
|
||||
sb.push('\0'); //Shift 1 Set
|
||||
sb.push(c);
|
||||
return 2;
|
||||
}
|
||||
if c <= '/' {
|
||||
sb.push('\u{1}'); //Shift 2 Set
|
||||
sb.push((c as u8 - 33) as char);
|
||||
return 2;
|
||||
}
|
||||
if c <= '@' {
|
||||
sb.push('\u{1}'); //Shift 2 Set
|
||||
sb.push((c as u8 - 58 + 15) as char);
|
||||
return 2;
|
||||
}
|
||||
if c >= '[' && c <= '_' {
|
||||
sb.push('\u{1}'); //Shift 2 Set
|
||||
sb.push((c as u8 - 91 + 22) as char);
|
||||
return 2;
|
||||
}
|
||||
if c == '`' {
|
||||
sb.push('\u{2}'); //Shift 3 Set
|
||||
sb.push(0 as char); // '`' - 96 == 0
|
||||
return 2;
|
||||
}
|
||||
if c <= 'Z' {
|
||||
sb.push('\u{2}'); //Shift 3 Set
|
||||
sb.push((c as u8 - 65 + 1) as char);
|
||||
return 2;
|
||||
}
|
||||
if c as u8 <= 127 {
|
||||
sb.push('\u{2}'); //Shift 3 Set
|
||||
sb.push((c as u8 - 123 + 27) as char);
|
||||
return 2;
|
||||
}
|
||||
sb.push_str("\u{1}\u{001e}"); //Shift 2, Upper Shift
|
||||
let len = 2;
|
||||
len += Self::encodeChar((c as u8 - 128) as char, sb);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user