You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

555 lines
20 KiB

/*
DataMatrix.Net
DataMatrix.Net - .net library for decoding DataMatrix codes.
Copyright (C) 2009/2010 Michael Faschinger
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
You can also redistribute and/or modify it under the terms of the
GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License or (at your option)
any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License or the GNU Lesser General Public License
for more details.
You should have received a copy of the GNU General Public
License and the GNU Lesser General Public License along with this
library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Contact: Michael Faschinger - michfasch@gmx.at
*/
using System;
namespace DataMatrix.net
{
internal class DmtxMessage
{
#region Fields
int _outputIdx; /* Internal index used to store output progress */
#endregion
#region Constructors
internal DmtxMessage(DmtxSymbolSize sizeIdx, DmtxFormat symbolFormat)
{
if (symbolFormat != DmtxFormat.Matrix && symbolFormat != DmtxFormat.Mosaic)
{
throw new ArgumentException("Only DmtxFormats Matrix and Mosaic are currently supported");
}
int mappingRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixRows, sizeIdx);
int mappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, sizeIdx);
this.Array = new byte[mappingCols * mappingRows];
int codeSize = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx) + DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolErrorWords, sizeIdx);
this.Code = new byte[codeSize];
this.Output = new byte[10 * codeSize];
}
#endregion
#region Methods
internal void DecodeDataStream(DmtxSymbolSize sizeIdx, byte[] outputStart)
{
bool macro = false;
this.Output = outputStart ?? this.Output;
this._outputIdx = 0;
byte[] ptr = this.Code;
int dataEndIndex = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx);
/* Print macro header if first codeword triggers it */
if (ptr[0] == DmtxConstants.DmtxChar05Macro || ptr[0] == DmtxConstants.DmtxChar06Macro)
{
PushOutputMacroHeader(ptr[0]);
macro = true;
}
for (int codeIter = 0; codeIter < dataEndIndex; )
{
DmtxScheme encScheme = GetEncodationScheme(this.Code[codeIter]);
if (encScheme != DmtxScheme.DmtxSchemeAscii)
codeIter++;
switch (encScheme)
{
case DmtxScheme.DmtxSchemeAscii:
codeIter = DecodeSchemeAscii(codeIter, dataEndIndex);
break;
case DmtxScheme.DmtxSchemeC40:
case DmtxScheme.DmtxSchemeText:
codeIter = DecodeSchemeC40Text(codeIter, dataEndIndex, encScheme);
break;
case DmtxScheme.DmtxSchemeX12:
codeIter = DecodeSchemeX12(codeIter, dataEndIndex);
break;
case DmtxScheme.DmtxSchemeEdifact:
codeIter = DecodeSchemeEdifact(codeIter, dataEndIndex);
break;
case DmtxScheme.DmtxSchemeBase256:
codeIter = DecodeSchemeBase256(codeIter, dataEndIndex);
break;
}
}
/* Print macro trailer if required */
if (macro)
{
PushOutputMacroTrailer();
}
}
private void PushOutputMacroHeader(byte macroType)
{
PushOutputWord((byte)'[');
PushOutputWord((byte)')');
PushOutputWord((byte)'>');
PushOutputWord(30); /* ASCII RS */
PushOutputWord((byte)'0');
if (macroType == DmtxConstants.DmtxChar05Macro)
{
PushOutputWord((byte)'5');
}
else if (macroType == DmtxConstants.DmtxChar06Macro)
{
PushOutputWord((byte)'6');
}
else
{
throw new ArgumentException("Macro Header only supported for char05 and char06");
}
PushOutputWord(29); /* ASCII GS */
}
void PushOutputMacroTrailer()
{
PushOutputWord(30); /* ASCII RS */
PushOutputWord(4); /* ASCII EOT */
}
void PushOutputWord(byte value)
{
this.Output[this._outputIdx++] = value;
}
static DmtxScheme GetEncodationScheme(byte val)
{
if (val == DmtxConstants.DmtxCharC40Latch)
{
return DmtxScheme.DmtxSchemeC40;
}
if (val == DmtxConstants.DmtxCharBase256Latch)
{
return DmtxScheme.DmtxSchemeBase256;
}
if (val == DmtxConstants.DmtxCharEdifactLatch)
{
return DmtxScheme.DmtxSchemeEdifact;
}
if (val == DmtxConstants.DmtxCharTextLatch)
{
return DmtxScheme.DmtxSchemeText;
}
if (val == DmtxConstants.DmtxCharX12Latch)
{
return DmtxScheme.DmtxSchemeX12;
}
return DmtxScheme.DmtxSchemeAscii;
}
int DecodeSchemeAscii(int startIndex, int endIndex)
{
bool upperShift = false;
while (startIndex < endIndex)
{
byte codeword = this.Code[startIndex];
if (GetEncodationScheme(this.Code[startIndex]) != DmtxScheme.DmtxSchemeAscii)
return startIndex;
startIndex++;
if (upperShift)
{
PushOutputWord((byte)(codeword + 127));
upperShift = false;
}
else if (codeword == DmtxConstants.DmtxCharAsciiUpperShift)
{
upperShift = true;
}
else if (codeword == DmtxConstants.DmtxCharAsciiPad)
{
this.PadCount = endIndex - startIndex;
return endIndex;
}
else if (codeword <= 128)
{
PushOutputWord((byte)(codeword - 1));
}
else if (codeword <= 229)
{
byte digits = (byte)(codeword - 130);
PushOutputWord((byte)(digits / 10 + '0'));
PushOutputWord((byte)(digits - (digits / 10) * 10 + '0'));
}
}
return startIndex;
}
int DecodeSchemeC40Text(int startIndex, int endIndex, DmtxScheme encScheme)
{
int[] c40Values = new int[3];
C40TextState state = new C40TextState {Shift = DmtxConstants.DmtxC40TextBasicSet, UpperShift = false};
if (!(encScheme == DmtxScheme.DmtxSchemeC40 || encScheme == DmtxScheme.DmtxSchemeText))
{
throw new ArgumentException("Invalid scheme selected for decodind!");
}
while (startIndex < endIndex)
{
/* FIXME Also check that ptr+1 is safe to access */
int packed = (this.Code[startIndex] << 8) | this.Code[startIndex + 1];
c40Values[0] = ((packed - 1) / 1600);
c40Values[1] = ((packed - 1) / 40) % 40;
c40Values[2] = (packed - 1) % 40;
startIndex += 2;
int i;
for (i = 0; i < 3; i++)
{
if (state.Shift == DmtxConstants.DmtxC40TextBasicSet)
{ /* Basic set */
if (c40Values[i] <= 2)
{
state.Shift = c40Values[i] + 1;
}
else if (c40Values[i] == 3)
{
PushOutputC40TextWord(ref state, ' ');
}
else if (c40Values[i] <= 13)
{
PushOutputC40TextWord(ref state, c40Values[i] - 13 + '9'); /* 0-9 */
}
else if (c40Values[i] <= 39)
{
if (encScheme == DmtxScheme.DmtxSchemeC40)
{
PushOutputC40TextWord(ref state, c40Values[i] - 39 + 'Z'); /* A-Z */
}
else if (encScheme == DmtxScheme.DmtxSchemeText)
{
PushOutputC40TextWord(ref state, c40Values[i] - 39 + 'z'); /* a-z */
}
}
}
else if (state.Shift == DmtxConstants.DmtxC40TextShift1)
{ /* Shift 1 set */
PushOutputC40TextWord(ref state, c40Values[i]); /* ASCII 0 - 31 */
}
else if (state.Shift == DmtxConstants.DmtxC40TextShift2)
{ /* Shift 2 set */
if (c40Values[i] <= 14)
{
PushOutputC40TextWord(ref state, c40Values[i] + 33); /* ASCII 33 - 47 */
}
else if (c40Values[i] <= 21)
{
PushOutputC40TextWord(ref state, c40Values[i] + 43); /* ASCII 58 - 64 */
}
else if (c40Values[i] <= 26)
{
PushOutputC40TextWord(ref state, c40Values[i] + 69); /* ASCII 91 - 95 */
}
else if (c40Values[i] == 27)
{
PushOutputC40TextWord(ref state, 0x1d); /* FNC1 -- XXX depends on position? */
}
else if (c40Values[i] == 30)
{
state.UpperShift = true;
state.Shift = DmtxConstants.DmtxC40TextBasicSet;
}
}
else if (state.Shift == DmtxConstants.DmtxC40TextShift3)
{ /* Shift 3 set */
if (encScheme == DmtxScheme.DmtxSchemeC40)
{
PushOutputC40TextWord(ref state, c40Values[i] + 96);
}
else if (encScheme == DmtxScheme.DmtxSchemeText)
{
if (c40Values[i] == 0)
PushOutputC40TextWord(ref state, c40Values[i] + 96);
else if (c40Values[i] <= 26)
PushOutputC40TextWord(ref state, c40Values[i] - 26 + 'Z'); /* A-Z */
else
PushOutputC40TextWord(ref state, c40Values[i] - 31 + 127); /* { | } ~ DEL */
}
}
}
/* Unlatch if codeword 254 follows 2 codewords in C40/Text encodation */
if (this.Code[startIndex] == DmtxConstants.DmtxCharTripletUnlatch)
return startIndex + 1;
/* Unlatch is implied if only one codeword remains */
if (endIndex - startIndex == 1)
return startIndex;
}
return startIndex;
}
void PushOutputC40TextWord(ref C40TextState state, int value)
{
if (!(value >= 0 && value < 256))
{
throw new ArgumentException("Invalid value: Exceeds range for conversion to byte");
}
this.Output[this._outputIdx] = (byte)value;
if (state.UpperShift)
{
if (!(value >= 0 && value < 256))
{
throw new ArgumentException("Invalid value: Exceeds range for conversion to upper case character");
}
this.Output[this._outputIdx] += 128;
}
this._outputIdx++;
state.Shift = DmtxConstants.DmtxC40TextBasicSet;
state.UpperShift = false;
}
private int DecodeSchemeX12(int startIndex, int endIndex)
{
int[] x12Values = new int[3];
while (startIndex < endIndex)
{
/* FIXME Also check that ptr+1 is safe to access */
int packed = (this.Code[startIndex] << 8) | this.Code[startIndex + 1];
x12Values[0] = ((packed - 1) / 1600);
x12Values[1] = ((packed - 1) / 40) % 40;
x12Values[2] = (packed - 1) % 40;
startIndex += 2;
for (int i = 0; i < 3; i++)
{
if (x12Values[i] == 0)
PushOutputWord(13);
else if (x12Values[i] == 1)
PushOutputWord(42);
else if (x12Values[i] == 2)
PushOutputWord(62);
else if (x12Values[i] == 3)
PushOutputWord(32);
else if (x12Values[i] <= 13)
PushOutputWord((byte)(x12Values[i] + 44));
else if (x12Values[i] <= 90)
PushOutputWord((byte)(x12Values[i] + 51));
}
/* Unlatch if codeword 254 follows 2 codewords in C40/Text encodation */
if (this.Code[startIndex] == DmtxConstants.DmtxCharTripletUnlatch)
return startIndex + 1;
/* Unlatch is implied if only one codeword remains */
if (endIndex - startIndex == 1)
return startIndex;
}
return startIndex;
}
int DecodeSchemeEdifact(int startIndex, int endIndex)
{
byte[] unpacked = new byte[4];
while (startIndex < endIndex)
{
/* FIXME Also check that ptr+2 is safe to access -- shouldn't be a
problem because I'm guessing you can guarantee there will always
be at least 3 error codewords */
unpacked[0] = (byte)((this.Code[startIndex] & 0xfc) >> 2);
unpacked[1] = (byte)((this.Code[startIndex] & 0x03) << 4 | (this.Code[startIndex + 1] & 0xf0) >> 4);
unpacked[2] = (byte)((this.Code[startIndex + 1] & 0x0f) << 2 | (this.Code[startIndex + 2] & 0xc0) >> 6);
unpacked[3] = (byte)(this.Code[startIndex + 2] & 0x3f);
for (int i = 0; i < 4; i++)
{
/* Advance input ptr (4th value comes from already-read 3rd byte) */
if (i < 3)
startIndex++;
/* Test for unlatch condition */
if (unpacked[i] == DmtxConstants.DmtxCharEdifactUnlatch)
{
if (this.Output[_outputIdx] != 0)
{/* XXX dirty why? */
throw new Exception("Error decoding edifact scheme");
}
return startIndex;
}
PushOutputWord((byte)(unpacked[i] ^ (((unpacked[i] & 0x20) ^ 0x20) << 1)));
}
/* Unlatch is implied if fewer than 3 codewords remain */
if (endIndex - startIndex < 3)
{
return startIndex;
}
}
return startIndex;
}
int DecodeSchemeBase256(int startIndex, int endIndex)
{
int tempEndIndex;
/* Find positional index used for unrandomizing */
int idx = startIndex + 1;
int d0 = UnRandomize255State(this.Code[startIndex++], idx++);
if (d0 == 0)
{
tempEndIndex = endIndex;
}
else if (d0 <= 249)
{
tempEndIndex = startIndex + d0;
}
else
{
int d1 = UnRandomize255State(this.Code[startIndex++], idx++);
tempEndIndex = startIndex + (d0 - 249) * 250 + d1;
}
if (tempEndIndex > endIndex)
{
throw new Exception("Error decoding scheme base 256");
}
while (startIndex < tempEndIndex)
{
PushOutputWord(UnRandomize255State(this.Code[startIndex++], idx++));
}
return startIndex;
}
internal static byte UnRandomize255State(byte value, int idx)
{
int pseudoRandom = ((149 * idx) % 255) + 1;
int tmp = value - pseudoRandom;
if (tmp < 0)
tmp += 256;
if (tmp < 0 || tmp >= 256)
{
throw new Exception("Error unrandomizing 255 state");
}
return (byte)tmp;
}
internal int SymbolModuleStatus(DmtxSymbolSize sizeIdx, int symbolRow, int symbolCol)
{
int dataRegionRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribDataRegionRows, sizeIdx);
int dataRegionCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribDataRegionCols, sizeIdx);
int symbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, sizeIdx);
int mappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, sizeIdx);
int symbolRowReverse = symbolRows - symbolRow - 1;
int mappingRow = symbolRowReverse - 1 - 2 * (symbolRowReverse / (dataRegionRows + 2));
int mappingCol = symbolCol - 1 - 2 * (symbolCol / (dataRegionCols + 2));
/* Solid portion of alignment patterns */
if (symbolRow % (dataRegionRows + 2) == 0 || symbolCol % (dataRegionCols + 2) == 0)
{
return (DmtxConstants.DmtxModuleOnRGB | (DmtxConstants.DmtxModuleData == 0 ? 1 : 0)); // check if unary not is correct
}
/* Horinzontal calibration bars */
if ((symbolRow + 1) % (dataRegionRows + 2) == 0)
{
return (((symbolCol & 0x01) != 0) ? 0 : DmtxConstants.DmtxModuleOnRGB) | (DmtxConstants.DmtxModuleData == 0 ? 1 : 0);
}
/* Vertical calibration bars */
if ((symbolCol + 1) % (dataRegionCols + 2) == 0)
{
return (((symbolRow & 0x01) != 0) ? 0 : DmtxConstants.DmtxModuleOnRGB) | (DmtxConstants.DmtxModuleData == 0 ? 1 : 0);
}
/* Data modules */
return (this.Array[mappingRow * mappingCols + mappingCol] | DmtxConstants.DmtxModuleData);
}
#endregion
#region Properties
internal int PadCount { get; set; }
internal byte[] Array { get; set; }
internal byte[] Code { get; set; }
internal byte[] Output { get; set; }
internal int ArraySize
{
get
{
return this.Array.Length;
}
}
internal int CodeSize
{
get
{
return this.Code.Length;
}
}
internal int OutputSize
{
get
{
return this.Output.Length;
}
}
#endregion
}
}