blob: 869e659f75079fadf57cc8e4cfe5682a198c4c1d [file] [log] [blame]
/*
* 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;
}
@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 || 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, 0));
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 >= '!' && c <= '/') {
sb.append('\1'); //Shift 2 Set
sb.append((char) (c - 33));
return 2;
}
if (c >= ':' && 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 >= '`' && 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 startPos) {
char c1 = sb.charAt(startPos);
char c2 = sb.charAt(startPos + 1);
char c3 = sb.charAt(startPos + 2);
int v = (1600 * c1) + (40 * c2) + c3 + 1;
char cw1 = (char) (v / 256);
char cw2 = (char) (v % 256);
return new String(new char[] {cw1, cw2});
}
}