/* * 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. */ using System; using System.Text; namespace ZXing.Datamatrix.Encoder { internal class C40Encoder : Encoder { virtual public int EncodingMode { get { return Encodation.C40; } } virtual public void encode(EncoderContext context) { //step C var buffer = new StringBuilder(); while (context.HasMoreCharacters) { char c = context.CurrentChar; context.Pos++; int lastCharSize = encodeChar(c, buffer); int unwritten = (buffer.Length / 3) * 2; int curCodewordCount = context.CodewordCount + unwritten; context.updateSymbolInfo(curCodewordCount); int available = context.SymbolInfo.dataCapacity - curCodewordCount; if (!context.HasMoreCharacters) { //Avoid having a single C40 value in the last triplet var removed = new StringBuilder(); if ((buffer.Length % 3) == 2) { if (available < 2 || available > 2) { lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize); } } while ((buffer.Length % 3) == 1 && ((lastCharSize <= 3 && available != 1) || lastCharSize > 3)) { lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize); } break; } int count = buffer.Length; if ((count % 3) == 0) { int newMode = HighLevelEncoder.lookAheadTest(context.Message, context.Pos, EncodingMode); if (newMode != EncodingMode) { context.signalEncoderChange(newMode); break; } } } handleEOD(context, buffer); } private int backtrackOneCharacter(EncoderContext context, StringBuilder buffer, StringBuilder removed, int lastCharSize) { int count = buffer.Length; buffer.Remove(count - lastCharSize, lastCharSize); context.Pos--; char c = context.CurrentChar; lastCharSize = encodeChar(c, removed); context.resetSymbolInfo(); //Deal with possible reduction in symbol size return lastCharSize; } internal static void writeNextTriplet(EncoderContext context, StringBuilder buffer) { context.writeCodewords(encodeToCodewords(buffer, 0)); buffer.Remove(0, 3); } /// /// Handle "end of data" situations /// /// the encoder context /// the buffer with the remaining encoded characters virtual protected void handleEOD(EncoderContext context, StringBuilder buffer) { int unwritten = (buffer.Length / 3) * 2; int rest = buffer.Length % 3; int curCodewordCount = context.CodewordCount + unwritten; context.updateSymbolInfo(curCodewordCount); int available = context.SymbolInfo.dataCapacity - curCodewordCount; if (rest == 2) { buffer.Append('\u0000'); //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 InvalidOperationException("Unexpected case. Please report!"); } context.signalEncoderChange(Encodation.ASCII); } virtual protected int encodeChar(char c, StringBuilder sb) { if (c == ' ') { sb.Append('\u0003'); 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 >= '\u0000' && c <= '\u001f') { sb.Append('\u0000'); //Shift 1 Set sb.Append(c); return 2; } if (c >= '!' && c <= '/') { sb.Append('\u0001'); //Shift 2 Set sb.Append((char)(c - 33)); return 2; } if (c >= ':' && c <= '@') { sb.Append('\u0001'); //Shift 2 Set sb.Append((char)(c - 58 + 15)); return 2; } if (c >= '[' && c <= '_') { sb.Append('\u0001'); //Shift 2 Set sb.Append((char)(c - 91 + 22)); return 2; } if (c >= '\u0060' && c <= '\u007f') { sb.Append('\u0002'); //Shift 3 Set sb.Append((char)(c - 96)); return 2; } if (c >= '\u0080') { sb.Append("\u0001\u001e"); //Shift 2, Upper Shift int len = 2; len += encodeChar((char)(c - 128), sb); return len; } throw new InvalidOperationException("Illegal character: " + c); } private static String encodeToCodewords(StringBuilder sb, int startPos) { char c1 = sb[startPos]; char c2 = sb[startPos + 1]; char c3 = sb[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 }); } } }