添加项目文件。

This commit is contained in:
akwkevin
2021-07-23 09:42:22 +08:00
commit f25a958797
2798 changed files with 352360 additions and 0 deletions

View File

@@ -0,0 +1,519 @@
/*
* Copyright 2007 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.
*/
using System;
using ZXing.Common;
namespace ZXing.Datamatrix.Internal
{
/// <summary>
/// <author>bbrown@google.com (Brian Brown)</author>
/// </summary>
sealed class BitMatrixParser
{
private readonly BitMatrix mappingBitMatrix;
private readonly BitMatrix readMappingMatrix;
private readonly Version version;
/// <summary>
/// <param name="bitMatrix"><see cref="BitMatrix" />to parse</param>
/// <exception cref="FormatException">if dimension is < 8 or >144 or not 0 mod 2</exception>
/// </summary>
internal BitMatrixParser(BitMatrix bitMatrix)
{
int dimension = bitMatrix.Height;
if (dimension < 8 || dimension > 144 || (dimension & 0x01) != 0)
{
return;
}
version = readVersion(bitMatrix);
if (version != null)
{
mappingBitMatrix = extractDataRegion(bitMatrix);
readMappingMatrix = new BitMatrix(mappingBitMatrix.Width, mappingBitMatrix.Height);
}
}
public Version Version
{
get { return version; }
}
/// <summary>
/// <p>Creates the version object based on the dimension of the original bit matrix from
/// the datamatrix code.</p>
///
/// <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p>
///
/// <param name="bitMatrix">Original <see cref="BitMatrix" />including alignment patterns</param>
/// <returns><see cref="Version" />encapsulating the Data Matrix Code's "version"</returns>
/// <exception cref="FormatException">if the dimensions of the mapping matrix are not valid</exception>
/// Data Matrix dimensions.
/// </summary>
internal static Version readVersion(BitMatrix bitMatrix)
{
int numRows = bitMatrix.Height;
int numColumns = bitMatrix.Width;
return Version.getVersionForDimensions(numRows, numColumns);
}
/// <summary>
/// <p>Reads the bits in the <see cref="BitMatrix" />representing the mapping matrix (No alignment patterns)
/// in the correct order in order to reconstitute the codewords bytes contained within the
/// Data Matrix Code.</p>
///
/// <returns>bytes encoded within the Data Matrix Code</returns>
/// <exception cref="FormatException">if the exact number of bytes expected is not read</exception>
/// </summary>
internal byte[] readCodewords()
{
byte[] result = new byte[version.getTotalCodewords()];
int resultOffset = 0;
int row = 4;
int column = 0;
int numRows = mappingBitMatrix.Height;
int numColumns = mappingBitMatrix.Width;
bool corner1Read = false;
bool corner2Read = false;
bool corner3Read = false;
bool corner4Read = false;
// Read all of the codewords
do
{
// Check the four corner cases
if ((row == numRows) && (column == 0) && !corner1Read)
{
result[resultOffset++] = (byte)readCorner1(numRows, numColumns);
row -= 2;
column += 2;
corner1Read = true;
}
else if ((row == numRows - 2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read)
{
result[resultOffset++] = (byte)readCorner2(numRows, numColumns);
row -= 2;
column += 2;
corner2Read = true;
}
else if ((row == numRows + 4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read)
{
result[resultOffset++] = (byte)readCorner3(numRows, numColumns);
row -= 2;
column += 2;
corner3Read = true;
}
else if ((row == numRows - 2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read)
{
result[resultOffset++] = (byte)readCorner4(numRows, numColumns);
row -= 2;
column += 2;
corner4Read = true;
}
else
{
// Sweep upward diagonally to the right
do
{
if ((row < numRows) && (column >= 0) && !readMappingMatrix[column, row])
{
result[resultOffset++] = (byte)readUtah(row, column, numRows, numColumns);
}
row -= 2;
column += 2;
} while ((row >= 0) && (column < numColumns));
row += 1;
column += 3;
// Sweep downward diagonally to the left
do
{
if ((row >= 0) && (column < numColumns) && !readMappingMatrix[column, row])
{
result[resultOffset++] = (byte)readUtah(row, column, numRows, numColumns);
}
row += 2;
column -= 2;
} while ((row < numRows) && (column >= 0));
row += 3;
column += 1;
}
} while ((row < numRows) || (column < numColumns));
if (resultOffset != version.getTotalCodewords())
{
return null;
}
return result;
}
/// <summary>
/// <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>
///
/// <param name="row">Row to read in the mapping matrix</param>
/// <param name="column">Column to read in the mapping matrix</param>
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>value of the given bit in the mapping matrix</returns>
/// </summary>
bool readModule(int row, int column, int numRows, int numColumns)
{
// Adjust the row and column indices based on boundary wrapping
if (row < 0)
{
row += numRows;
column += 4 - ((numRows + 4) & 0x07);
}
if (column < 0)
{
column += numColumns;
row += 4 - ((numColumns + 4) & 0x07);
}
readMappingMatrix[column, row] = true;
return mappingBitMatrix[column, row];
}
/// <summary>
/// <p>Reads the 8 bits of the standard Utah-shaped pattern.</p>
///
/// <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
///
/// <param name="row">Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern</param>
/// <param name="column">Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern</param>
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the utah shape</returns>
/// </summary>
int readUtah(int row, int column, int numRows, int numColumns)
{
int currentByte = 0;
if (readModule(row - 2, column - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 2, column - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column, numRows, numColumns))
{
currentByte |= 1;
}
return currentByte;
}
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 1.</p>
///
/// <p>See ISO 16022:2006, Figure F.3</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 1</returns>
/// </summary>
int readCorner1(int numRows, int numColumns)
{
int currentByte = 0;
if (readModule(numRows - 1, 0, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(2, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(3, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
return currentByte;
}
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 2.</p>
///
/// <p>See ISO 16022:2006, Figure F.4</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 2</returns>
/// </summary>
int readCorner2(int numRows, int numColumns)
{
int currentByte = 0;
if (readModule(numRows - 3, 0, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 2, 0, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 0, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 4, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 3, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
return currentByte;
}
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 3.</p>
///
/// <p>See ISO 16022:2006, Figure F.5</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 3</returns>
/// </summary>
int readCorner3(int numRows, int numColumns)
{
int currentByte = 0;
if (readModule(numRows - 1, 0, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 3, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 3, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
return currentByte;
}
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 4.</p>
///
/// <p>See ISO 16022:2006, Figure F.6</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 4</returns>
/// </summary>
int readCorner4(int numRows, int numColumns)
{
int currentByte = 0;
if (readModule(numRows - 3, 0, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 2, 0, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 0, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(2, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(3, numColumns - 1, numRows, numColumns))
{
currentByte |= 1;
}
return currentByte;
}
/// <summary>
/// <p>Extracts the data region from a <see cref="BitMatrix" />that contains
/// alignment patterns.</p>
///
/// <param name="bitMatrix">Original <see cref="BitMatrix" />with alignment patterns</param>
/// <returns>BitMatrix that has the alignment patterns removed</returns>
/// </summary>
BitMatrix extractDataRegion(BitMatrix bitMatrix)
{
int symbolSizeRows = version.getSymbolSizeRows();
int symbolSizeColumns = version.getSymbolSizeColumns();
if (bitMatrix.Height != symbolSizeRows)
{
throw new ArgumentException("Dimension of bitMarix must match the version size");
}
int dataRegionSizeRows = version.getDataRegionSizeRows();
int dataRegionSizeColumns = version.getDataRegionSizeColumns();
int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows;
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow);
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow)
{
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn)
{
int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns;
for (int i = 0; i < dataRegionSizeRows; ++i)
{
int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
int writeRowOffset = dataRegionRowOffset + i;
for (int j = 0; j < dataRegionSizeColumns; ++j)
{
int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
if (bitMatrix[readColumnOffset, readRowOffset])
{
int writeColumnOffset = dataRegionColumnOffset + j;
bitMatrixWithoutAlignment[writeColumnOffset, writeRowOffset] = true;
}
}
}
}
}
return bitMatrixWithoutAlignment;
}
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2008 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.
*/
using System;
namespace ZXing.Datamatrix.Internal
{
/// <summary>
/// <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into
/// multiple blocks, each of which is a unit of data and error-correction codewords. Each
/// is represented by an instance of this class.</p>
///
/// <author>bbrown@google.com (Brian Brown)</author>
/// </summary>
internal sealed class DataBlock
{
private readonly int numDataCodewords;
private readonly byte[] codewords;
private DataBlock(int numDataCodewords, byte[] codewords)
{
this.numDataCodewords = numDataCodewords;
this.codewords = codewords;
}
/// <summary>
/// <p>When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them.
/// That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
/// method will separate the data into original blocks.</p>
///
/// <param name="rawCodewords">bytes as read directly from the Data Matrix Code</param>
/// <param name="version">version of the Data Matrix Code</param>
/// <returns>DataBlocks containing original bytes, "de-interleaved" from representation in the</returns>
/// Data Matrix Code
/// </summary>
internal static DataBlock[] getDataBlocks(byte[] rawCodewords,
Version version)
{
// Figure out the number and size of data blocks used by this version
Version.ECBlocks ecBlocks = version.getECBlocks();
// First count the total number of data blocks
int totalBlocks = 0;
Version.ECB[] ecBlockArray = ecBlocks.ECBlocksValue;
foreach (Version.ECB ecBlock in ecBlockArray)
{
totalBlocks += ecBlock.Count;
}
// Now establish DataBlocks of the appropriate size and number of data codewords
DataBlock[] result = new DataBlock[totalBlocks];
int numResultBlocks = 0;
foreach (Version.ECB ecBlock in ecBlockArray)
{
for (int i = 0; i < ecBlock.Count; i++)
{
int numDataCodewords = ecBlock.DataCodewords;
int numBlockCodewords = ecBlocks.ECCodewords + numDataCodewords;
result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
}
}
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 less byte. Figure out where these start.
// TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144
int longerBlocksTotalCodewords = result[0].codewords.Length;
//int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1;
int longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.ECCodewords;
int shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1;
// The last elements of result may be 1 element shorter for 144 matrix
// first fill out as many elements as all of them have minus 1
int rawCodewordsOffset = 0;
for (int i = 0; i < shorterBlocksNumDataCodewords; i++)
{
for (int j = 0; j < numResultBlocks; j++)
{
result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
}
}
// Fill out the last data block in the longer ones
bool specialVersion = version.getVersionNumber() == 24;
int numLongerBlocks = specialVersion ? 8 : numResultBlocks;
for (int j = 0; j < numLongerBlocks; j++)
{
result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++];
}
// Now add in error correction blocks
int max = result[0].codewords.Length;
for (int i = longerBlocksNumDataCodewords; i < max; i++)
{
for (int j = 0; j < numResultBlocks; j++)
{
int jOffset = specialVersion ? (j + 8)%numResultBlocks : j;
int iOffset = specialVersion && jOffset > 7 ? i - 1 : i;
result[jOffset].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
}
}
if (rawCodewordsOffset != rawCodewords.Length)
{
throw new ArgumentException();
}
return result;
}
internal int NumDataCodewords
{
get { return numDataCodewords; }
}
internal byte[] Codewords
{
get { return codewords; }
}
}
}

View File

@@ -0,0 +1,705 @@
/*
* Copyright 2008 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.
*/
using System;
using System.Collections.Generic;
using System.Text;
using ZXing.Common;
namespace ZXing.Datamatrix.Internal
{
/// <summary>
/// <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes
/// in one Data Matrix Code. This class decodes the bits back into text.</p>
///
/// <p>See ISO 16022:2006, 5.2.1 - 5.2.9.2</p>
///
/// <author>bbrown@google.com (Brian Brown)</author>
/// <author>Sean Owen</author>
/// </summary>
internal static class DecodedBitStreamParser
{
private enum Mode
{
PAD_ENCODE, // Not really a mode
ASCII_ENCODE,
C40_ENCODE,
TEXT_ENCODE,
ANSIX12_ENCODE,
EDIFACT_ENCODE,
BASE256_ENCODE
}
/// <summary>
/// See ISO 16022:2006, Annex C Table C.1
/// The C40 Basic Character Set (*'s used for placeholders for the shift values)
/// </summary>
private static readonly char[] C40_BASIC_SET_CHARS =
{
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
private static readonly char[] C40_SHIFT2_SET_CHARS =
{
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'
};
/// <summary>
/// See ISO 16022:2006, Annex C Table C.2
/// The Text Basic Character Set (*'s used for placeholders for the shift values)
/// </summary>
private static readonly char[] TEXT_BASIC_SET_CHARS =
{
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
};
// Shift 2 for Text is the same encoding as C40
private static readonly char[] TEXT_SHIFT2_SET_CHARS = C40_SHIFT2_SET_CHARS;
private static readonly char[] TEXT_SHIFT3_SET_CHARS =
{
'`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{',
'|', '}', '~', (char) 127
};
internal static DecoderResult decode(byte[] bytes)
{
BitSource bits = new BitSource(bytes);
StringBuilder result = new StringBuilder(100);
StringBuilder resultTrailer = new StringBuilder(0);
List<byte[]> byteSegments = new List<byte[]>(1);
Mode mode = Mode.ASCII_ENCODE;
do
{
if (mode == Mode.ASCII_ENCODE)
{
if (!decodeAsciiSegment(bits, result, resultTrailer, out mode))
return null;
}
else
{
switch (mode)
{
case Mode.C40_ENCODE:
if (!decodeC40Segment(bits, result))
return null;
break;
case Mode.TEXT_ENCODE:
if (!decodeTextSegment(bits, result))
return null;
break;
case Mode.ANSIX12_ENCODE:
if (!decodeAnsiX12Segment(bits, result))
return null;
break;
case Mode.EDIFACT_ENCODE:
if (!decodeEdifactSegment(bits, result))
return null;
break;
case Mode.BASE256_ENCODE:
if (!decodeBase256Segment(bits, result, byteSegments))
return null;
break;
default:
return null;
}
mode = Mode.ASCII_ENCODE;
}
} while (mode != Mode.PAD_ENCODE && bits.available() > 0);
if (resultTrailer.Length > 0)
{
result.Append(resultTrailer.ToString());
}
return new DecoderResult(bytes, result.ToString(), byteSegments.Count == 0 ? null : byteSegments, null);
}
/// <summary>
/// See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
/// </summary>
private static bool decodeAsciiSegment(BitSource bits,
StringBuilder result,
StringBuilder resultTrailer,
out Mode mode)
{
bool upperShift = false;
mode = Mode.ASCII_ENCODE;
do
{
int oneByte = bits.readBits(8);
if (oneByte == 0)
{
return false;
}
else if (oneByte <= 128)
{
// ASCII data (ASCII value + 1)
if (upperShift)
{
oneByte += 128;
//upperShift = false;
}
result.Append((char) (oneByte - 1));
mode = Mode.ASCII_ENCODE;
return true;
}
else if (oneByte == 129)
{
// Pad
mode = Mode.PAD_ENCODE;
return true;
}
else if (oneByte <= 229)
{
// 2-digit data 00-99 (Numeric Value + 130)
int value = oneByte - 130;
if (value < 10)
{
// pad with '0' for single digit values
result.Append('0');
}
result.Append(value);
}
else if (oneByte == 230)
{
// Latch to C40 encodation
mode = Mode.C40_ENCODE;
return true;
}
else if (oneByte == 231)
{
// Latch to Base 256 encodation
mode = Mode.BASE256_ENCODE;
return true;
}
else if (oneByte == 232)
{
// FNC1
result.Append((char) 29); // translate as ASCII 29
}
else if (oneByte == 233 || oneByte == 234)
{
// Structured Append, Reader Programming
// Ignore these symbols for now
//throw ReaderException.Instance;
}
else if (oneByte == 235)
{
// Upper Shift (shift to Extended ASCII)
upperShift = true;
}
else if (oneByte == 236)
{
// 05 Macro
result.Append("[)>\u001E05\u001D");
resultTrailer.Insert(0, "\u001E\u0004");
}
else if (oneByte == 237)
{
// 06 Macro
result.Append("[)>\u001E06\u001D");
resultTrailer.Insert(0, "\u001E\u0004");
}
else if (oneByte == 238)
{
// Latch to ANSI X12 encodation
mode = Mode.ANSIX12_ENCODE;
return true;
}
else if (oneByte == 239)
{
// Latch to Text encodation
mode = Mode.TEXT_ENCODE;
return true;
}
else if (oneByte == 240)
{
// Latch to EDIFACT encodation
mode = Mode.EDIFACT_ENCODE;
return true;
}
else if (oneByte == 241)
{
// ECI Character
// TODO(bbrown): I think we need to support ECI
//throw ReaderException.Instance;
// Ignore this symbol for now
}
else if (oneByte >= 242)
{
// Not to be used in ASCII encodation
// ... but work around encoders that end with 254, latch back to ASCII
if (oneByte != 254 || bits.available() != 0)
{
return false;
}
}
} while (bits.available() > 0);
mode = Mode.ASCII_ENCODE;
return true;
}
/// <summary>
/// See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
/// </summary>
private static bool decodeC40Segment(BitSource bits, StringBuilder result)
{
// Three C40 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
bool upperShift = false;
int[] cValues = new int[3];
int shift = 0;
do
{
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8)
{
return true;
}
int firstByte = bits.readBits(8);
if (firstByte == 254)
{
// Unlatch codeword
return true;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
for (int i = 0; i < 3; i++)
{
int cValue = cValues[i];
switch (shift)
{
case 0:
if (cValue < 3)
{
shift = cValue + 1;
}
else if (cValue < C40_BASIC_SET_CHARS.Length)
{
char c40char = C40_BASIC_SET_CHARS[cValue];
if (upperShift)
{
result.Append((char) (c40char + 128));
upperShift = false;
}
else
{
result.Append(c40char);
}
}
else
{
return false;
}
break;
case 1:
if (upperShift)
{
result.Append((char) (cValue + 128));
upperShift = false;
}
else
{
result.Append((char) cValue);
}
shift = 0;
break;
case 2:
if (cValue < C40_SHIFT2_SET_CHARS.Length)
{
char c40char = C40_SHIFT2_SET_CHARS[cValue];
if (upperShift)
{
result.Append((char) (c40char + 128));
upperShift = false;
}
else
{
result.Append(c40char);
}
}
else if (cValue == 27)
{
// FNC1
result.Append((char) 29); // translate as ASCII 29
}
else if (cValue == 30)
{
// Upper Shift
upperShift = true;
}
else
{
return false;
}
shift = 0;
break;
case 3:
if (upperShift)
{
result.Append((char) (cValue + 224));
upperShift = false;
}
else
{
result.Append((char) (cValue + 96));
}
shift = 0;
break;
default:
return false;
}
}
} while (bits.available() > 0);
return true;
}
/// <summary>
/// See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
/// </summary>
private static bool decodeTextSegment(BitSource bits, StringBuilder result)
{
// Three Text values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
bool upperShift = false;
int[] cValues = new int[3];
int shift = 0;
do
{
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8)
{
return true;
}
int firstByte = bits.readBits(8);
if (firstByte == 254)
{
// Unlatch codeword
return true;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
for (int i = 0; i < 3; i++)
{
int cValue = cValues[i];
switch (shift)
{
case 0:
if (cValue < 3)
{
shift = cValue + 1;
}
else if (cValue < TEXT_BASIC_SET_CHARS.Length)
{
char textChar = TEXT_BASIC_SET_CHARS[cValue];
if (upperShift)
{
result.Append((char) (textChar + 128));
upperShift = false;
}
else
{
result.Append(textChar);
}
}
else
{
return false;
}
break;
case 1:
if (upperShift)
{
result.Append((char) (cValue + 128));
upperShift = false;
}
else
{
result.Append((char) cValue);
}
shift = 0;
break;
case 2:
// Shift 2 for Text is the same encoding as C40
if (cValue < TEXT_SHIFT2_SET_CHARS.Length)
{
char textChar = TEXT_SHIFT2_SET_CHARS[cValue];
if (upperShift)
{
result.Append((char) (textChar + 128));
upperShift = false;
}
else
{
result.Append(textChar);
}
}
else if (cValue == 27)
{
// FNC1
result.Append((char) 29); // translate as ASCII 29
}
else if (cValue == 30)
{
// Upper Shift
upperShift = true;
}
else
{
return false;
}
shift = 0;
break;
case 3:
if (cValue < TEXT_SHIFT3_SET_CHARS.Length)
{
char textChar = TEXT_SHIFT3_SET_CHARS[cValue];
if (upperShift)
{
result.Append((char) (textChar + 128));
upperShift = false;
}
else
{
result.Append(textChar);
}
shift = 0;
}
else
{
return false;
}
break;
default:
return false;
}
}
} while (bits.available() > 0);
return true;
}
/// <summary>
/// See ISO 16022:2006, 5.2.7
/// </summary>
private static bool decodeAnsiX12Segment(BitSource bits,
StringBuilder result)
{
// Three ANSI X12 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
int[] cValues = new int[3];
do
{
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() == 8)
{
return true;
}
int firstByte = bits.readBits(8);
if (firstByte == 254)
{
// Unlatch codeword
return true;
}
parseTwoBytes(firstByte, bits.readBits(8), cValues);
for (int i = 0; i < 3; i++)
{
int cValue = cValues[i];
if (cValue == 0)
{
// X12 segment terminator <CR>
result.Append('\r');
}
else if (cValue == 1)
{
// X12 segment separator *
result.Append('*');
}
else if (cValue == 2)
{
// X12 sub-element separator >
result.Append('>');
}
else if (cValue == 3)
{
// space
result.Append(' ');
}
else if (cValue < 14)
{
// 0 - 9
result.Append((char) (cValue + 44));
}
else if (cValue < 40)
{
// A - Z
result.Append((char) (cValue + 51));
}
else
{
return false;
}
}
} while (bits.available() > 0);
return true;
}
private static void parseTwoBytes(int firstByte, int secondByte, int[] result)
{
int fullBitValue = (firstByte << 8) + secondByte - 1;
int temp = fullBitValue/1600;
result[0] = temp;
fullBitValue -= temp*1600;
temp = fullBitValue/40;
result[1] = temp;
result[2] = fullBitValue - temp*40;
}
/// <summary>
/// See ISO 16022:2006, 5.2.8 and Annex C Table C.3
/// </summary>
private static bool decodeEdifactSegment(BitSource bits, StringBuilder result)
{
do
{
// If there is only two or less bytes left then it will be encoded as ASCII
if (bits.available() <= 16)
{
return true;
}
for (int i = 0; i < 4; i++)
{
int edifactValue = bits.readBits(6);
// Check for the unlatch character
if (edifactValue == 0x1F)
{
// 011111
// Read rest of byte, which should be 0, and stop
int bitsLeft = 8 - bits.BitOffset;
if (bitsLeft != 8)
{
bits.readBits(bitsLeft);
}
return true;
}
if ((edifactValue & 0x20) == 0)
{
// no 1 in the leading (6th) bit
edifactValue |= 0x40; // Add a leading 01 to the 6 bit binary value
}
result.Append((char) edifactValue);
}
} while (bits.available() > 0);
return true;
}
/// <summary>
/// See ISO 16022:2006, 5.2.9 and Annex B, B.2
/// </summary>
private static bool decodeBase256Segment(BitSource bits,
StringBuilder result,
IList<byte[]> byteSegments)
{
// Figure out how long the Base 256 Segment is.
int codewordPosition = 1 + bits.ByteOffset; // position is 1-indexed
int d1 = unrandomize255State(bits.readBits(8), codewordPosition++);
int count;
if (d1 == 0)
{
// Read the remainder of the symbol
count = bits.available()/8;
}
else if (d1 < 250)
{
count = d1;
}
else
{
count = 250*(d1 - 249) + unrandomize255State(bits.readBits(8), codewordPosition++);
}
// We're seeing NegativeArraySizeException errors from users.
if (count < 0)
{
return false;
}
byte[] bytes = new byte[count];
for (int i = 0; i < count; i++)
{
// Have seen this particular error in the wild, such as at
// http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2
if (bits.available() < 8)
{
return false;
}
bytes[i] = (byte) unrandomize255State(bits.readBits(8), codewordPosition++);
}
byteSegments.Add(bytes);
try
{
#if (WINDOWS_PHONE || SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || WindowsCE || PORTABLE)
#if WindowsCE
result.Append(Encoding.GetEncoding(1252).GetString(bytes, 0, bytes.Length));
#else
result.Append(Encoding.GetEncoding("ISO-8859-1").GetString(bytes, 0, bytes.Length));
#endif
#else
result.Append(Encoding.GetEncoding("ISO-8859-1").GetString(bytes));
#endif
}
catch (Exception uee)
{
throw new InvalidOperationException("Platform does not support required encoding: " + uee);
}
return true;
}
/// <summary>
/// See ISO 16022:2006, Annex B, B.2
/// </summary>
private static int unrandomize255State(int randomizedBase256Codeword,
int base256CodewordPosition)
{
int pseudoRandomNumber = ((149*base256CodewordPosition)%255) + 1;
int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;
return tempVariable >= 0 ? tempVariable : tempVariable + 256;
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2007 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.
*/
using ZXing.Common;
using ZXing.Common.ReedSolomon;
namespace ZXing.Datamatrix.Internal
{
/// <summary>
/// <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting
/// the Data Matrix Code from an image.</p>
///
/// <author>bbrown@google.com (Brian Brown)</author>
/// </summary>
public sealed class Decoder
{
private readonly ReedSolomonDecoder rsDecoder;
/// <summary>
/// Initializes a new instance of the <see cref="Decoder"/> class.
/// </summary>
public Decoder()
{
rsDecoder = new ReedSolomonDecoder(GenericGF.DATA_MATRIX_FIELD_256);
}
/// <summary>
/// <p>Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans.
/// "true" is taken to mean a black module.</p>
///
/// <param name="image">booleans representing white/black Data Matrix Code modules</param>
/// <returns>text and bytes encoded within the Data Matrix Code</returns>
/// <exception cref="FormatException">if the Data Matrix Code cannot be decoded</exception>
/// <exception cref="ChecksumException">if error correction fails</exception>
/// </summary>
public DecoderResult decode(bool[][] image)
{
int dimension = image.Length;
BitMatrix bits = new BitMatrix(dimension);
for (int i = 0; i < dimension; i++)
{
for (int j = 0; j < dimension; j++)
{
if (image[i][j])
{
bits[j, i] = true;
}
}
}
return decode(bits);
}
/// <summary>
/// <p>Decodes a Data Matrix Code represented as a <see cref="BitMatrix" />. A 1 or "true" is taken
/// to mean a black module.</p>
/// </summary>
/// <param name="bits">booleans representing white/black Data Matrix Code modules</param>
/// <returns>text and bytes encoded within the Data Matrix Code</returns>
public DecoderResult decode(BitMatrix bits)
{
// Construct a parser and read version, error-correction level
BitMatrixParser parser = new BitMatrixParser(bits);
if (parser.Version == null)
return null;
// Read codewords
byte[] codewords = parser.readCodewords();
if (codewords == null)
return null;
// Separate into data blocks
DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, parser.Version);
int dataBlocksCount = dataBlocks.Length;
// Count total number of data bytes
int totalBytes = 0;
foreach (var db in dataBlocks)
{
totalBytes += db.NumDataCodewords;
}
byte[] resultBytes = new byte[totalBytes];
// Error-correct and copy data blocks together into a stream of bytes
for (int j = 0; j < dataBlocksCount; j++)
{
DataBlock dataBlock = dataBlocks[j];
byte[] codewordBytes = dataBlock.Codewords;
int numDataCodewords = dataBlock.NumDataCodewords;
if (!correctErrors(codewordBytes, numDataCodewords))
return null;
for (int i = 0; i < numDataCodewords; i++)
{
// De-interlace data blocks.
resultBytes[i * dataBlocksCount + j] = codewordBytes[i];
}
}
// Decode the contents of that stream of bytes
return DecodedBitStreamParser.decode(resultBytes);
}
/// <summary>
/// <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
/// correct the errors in-place using Reed-Solomon error correction.</p>
///
/// <param name="codewordBytes">data and error correction codewords</param>
/// <param name="numDataCodewords">number of codewords that are data bytes</param>
/// </summary>
private bool correctErrors(byte[] codewordBytes, int numDataCodewords)
{
int numCodewords = codewordBytes.Length;
// First read into an array of ints
int[] codewordsInts = new int[numCodewords];
for (int i = 0; i < numCodewords; i++)
{
codewordsInts[i] = codewordBytes[i] & 0xFF;
}
int numECCodewords = codewordBytes.Length - numDataCodewords;
if (!rsDecoder.decode(codewordsInts, numECCodewords))
return false;
// Copy back into array of bytes -- only need to worry about the bytes that were data
// We don't care about errors in the error-correction codewords
for (int i = 0; i < numDataCodewords; i++)
{
codewordBytes[i] = (byte)codewordsInts[i];
}
return true;
}
}
}

View File

@@ -0,0 +1,261 @@
/*
* Copyright 2007 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.
*/
using System;
namespace ZXing.Datamatrix.Internal
{
/// <summary>
/// The Version object encapsulates attributes about a particular
/// size Data Matrix Code.
///
/// <author>bbrown@google.com (Brian Brown)</author>
/// </summary>
public sealed class Version
{
private static readonly Version[] VERSIONS = buildVersions();
private readonly int versionNumber;
private readonly int symbolSizeRows;
private readonly int symbolSizeColumns;
private readonly int dataRegionSizeRows;
private readonly int dataRegionSizeColumns;
private readonly ECBlocks ecBlocks;
private readonly int totalCodewords;
internal Version(int versionNumber,
int symbolSizeRows,
int symbolSizeColumns,
int dataRegionSizeRows,
int dataRegionSizeColumns,
ECBlocks ecBlocks)
{
this.versionNumber = versionNumber;
this.symbolSizeRows = symbolSizeRows;
this.symbolSizeColumns = symbolSizeColumns;
this.dataRegionSizeRows = dataRegionSizeRows;
this.dataRegionSizeColumns = dataRegionSizeColumns;
this.ecBlocks = ecBlocks;
// Calculate the total number of codewords
int total = 0;
int ecCodewords = ecBlocks.ECCodewords;
ECB[] ecbArray = ecBlocks.ECBlocksValue;
foreach (ECB ecBlock in ecbArray)
{
total += ecBlock.Count * (ecBlock.DataCodewords + ecCodewords);
}
this.totalCodewords = total;
}
public int getVersionNumber()
{
return versionNumber;
}
public int getSymbolSizeRows()
{
return symbolSizeRows;
}
public int getSymbolSizeColumns()
{
return symbolSizeColumns;
}
public int getDataRegionSizeRows()
{
return dataRegionSizeRows;
}
public int getDataRegionSizeColumns()
{
return dataRegionSizeColumns;
}
public int getTotalCodewords()
{
return totalCodewords;
}
internal ECBlocks getECBlocks()
{
return ecBlocks;
}
/// <summary>
/// <p>Deduces version information from Data Matrix dimensions.</p>
///
/// <param name="numRows">Number of rows in modules</param>
/// <param name="numColumns">Number of columns in modules</param>
/// <returns>Version for a Data Matrix Code of those dimensions</returns>
/// <exception cref="FormatException">if dimensions do correspond to a valid Data Matrix size</exception>
/// </summary>
public static Version getVersionForDimensions(int numRows, int numColumns)
{
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0)
{
return null;
}
foreach (var version in VERSIONS)
{
if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns)
{
return version;
}
}
return null;
}
/// <summary>
/// <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
/// use blocks of differing sizes within one version, so, this encapsulates the parameters for
/// each set of blocks. It also holds the number of error-correction codewords per block since it
/// will be the same across all blocks within one version.</p>
/// </summary>
internal sealed class ECBlocks
{
private readonly int ecCodewords;
private readonly ECB[] _ecBlocksValue;
internal ECBlocks(int ecCodewords, ECB ecBlocks)
{
this.ecCodewords = ecCodewords;
this._ecBlocksValue = new ECB[] { ecBlocks };
}
internal ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2)
{
this.ecCodewords = ecCodewords;
this._ecBlocksValue = new ECB[] { ecBlocks1, ecBlocks2 };
}
internal int ECCodewords
{
get { return ecCodewords; }
}
internal ECB[] ECBlocksValue
{
get { return _ecBlocksValue; }
}
}
/// <summary>
/// <p>Encapsualtes the parameters for one error-correction block in one symbol version.
/// This includes the number of data codewords, and the number of times a block with these
/// parameters is used consecutively in the Data Matrix code version's format.</p>
/// </summary>
internal sealed class ECB
{
private readonly int count;
private readonly int dataCodewords;
internal ECB(int count, int dataCodewords)
{
this.count = count;
this.dataCodewords = dataCodewords;
}
internal int Count
{
get { return count; }
}
internal int DataCodewords
{
get { return dataCodewords; }
}
}
override public String ToString()
{
return versionNumber.ToString();
}
/// <summary>
/// See ISO 16022:2006 5.5.1 Table 7
/// </summary>
private static Version[] buildVersions()
{
return new Version[]
{
new Version(1, 10, 10, 8, 8,
new ECBlocks(5, new ECB(1, 3))),
new Version(2, 12, 12, 10, 10,
new ECBlocks(7, new ECB(1, 5))),
new Version(3, 14, 14, 12, 12,
new ECBlocks(10, new ECB(1, 8))),
new Version(4, 16, 16, 14, 14,
new ECBlocks(12, new ECB(1, 12))),
new Version(5, 18, 18, 16, 16,
new ECBlocks(14, new ECB(1, 18))),
new Version(6, 20, 20, 18, 18,
new ECBlocks(18, new ECB(1, 22))),
new Version(7, 22, 22, 20, 20,
new ECBlocks(20, new ECB(1, 30))),
new Version(8, 24, 24, 22, 22,
new ECBlocks(24, new ECB(1, 36))),
new Version(9, 26, 26, 24, 24,
new ECBlocks(28, new ECB(1, 44))),
new Version(10, 32, 32, 14, 14,
new ECBlocks(36, new ECB(1, 62))),
new Version(11, 36, 36, 16, 16,
new ECBlocks(42, new ECB(1, 86))),
new Version(12, 40, 40, 18, 18,
new ECBlocks(48, new ECB(1, 114))),
new Version(13, 44, 44, 20, 20,
new ECBlocks(56, new ECB(1, 144))),
new Version(14, 48, 48, 22, 22,
new ECBlocks(68, new ECB(1, 174))),
new Version(15, 52, 52, 24, 24,
new ECBlocks(42, new ECB(2, 102))),
new Version(16, 64, 64, 14, 14,
new ECBlocks(56, new ECB(2, 140))),
new Version(17, 72, 72, 16, 16,
new ECBlocks(36, new ECB(4, 92))),
new Version(18, 80, 80, 18, 18,
new ECBlocks(48, new ECB(4, 114))),
new Version(19, 88, 88, 20, 20,
new ECBlocks(56, new ECB(4, 144))),
new Version(20, 96, 96, 22, 22,
new ECBlocks(68, new ECB(4, 174))),
new Version(21, 104, 104, 24, 24,
new ECBlocks(56, new ECB(6, 136))),
new Version(22, 120, 120, 18, 18,
new ECBlocks(68, new ECB(6, 175))),
new Version(23, 132, 132, 20, 20,
new ECBlocks(62, new ECB(8, 163))),
new Version(24, 144, 144, 22, 22,
new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))),
new Version(25, 8, 18, 6, 16,
new ECBlocks(7, new ECB(1, 5))),
new Version(26, 8, 32, 6, 14,
new ECBlocks(11, new ECB(1, 10))),
new Version(27, 12, 26, 10, 24,
new ECBlocks(14, new ECB(1, 16))),
new Version(28, 12, 36, 10, 16,
new ECBlocks(18, new ECB(1, 22))),
new Version(29, 16, 36, 14, 16,
new ECBlocks(24, new ECB(1, 32))),
new Version(30, 16, 48, 14, 22,
new ECBlocks(28, new ECB(1, 49)))
};
}
}
}