zxing/csharp/pdf417/decoder/BitMatrixParser.cs

360 lines
50 KiB
C#
Raw Normal View History

using System;
/*
* Copyright 2009 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.
*/
namespace com.google.zxing.pdf417.decoder
{
using FormatException = com.google.zxing.FormatException;
using BitMatrix = com.google.zxing.common.BitMatrix;
/// <summary>
/// <p>
/// This class parses the BitMatrix image into codewords.
/// </p>
///
/// @author SITA Lab (kevin.osullivan@sita.aero)
/// </summary>
internal sealed class BitMatrixParser
{
private static readonly int[] NO_ERRORS = new int[0];
private const int MAX_ROW_DIFFERENCE = 6;
private const int MAX_ROWS = 90;
//private static final int MAX_COLUMNS = 30;
// Maximum Codewords (Data + Error)
private const int MAX_CW_CAPACITY = 929;
private const int MODULES_IN_SYMBOL = 17;
private readonly BitMatrix bitMatrix;
private int rows = 0;
//private int columns = 0;
private int leftColumnECData = 0;
private int rightColumnECData = 0;
private int eraseCount = 0;
private int[] erasures;
private int ecLevel = -1;
internal BitMatrixParser(BitMatrix bitMatrix)
{
this.bitMatrix = bitMatrix;
}
/// <summary>
/// To ensure separability of rows, codewords of consecutive rows belong to
/// different subsets of all possible codewords. This routine scans the
/// symbols in the barcode. When it finds a number of consecutive rows which
/// are the same, it assumes that this is a row of codewords and processes
/// them into a codeword array.
/// </summary>
/// <returns> an array of codewords. </returns>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: int[] readCodewords() throws com.google.zxing.FormatException
internal int[] readCodewords()
{
int width = bitMatrix.Width;
int height = bitMatrix.Height;
erasures = new int[MAX_CW_CAPACITY];
// Get the number of pixels in a module across the X dimension
//float moduleWidth = bitMatrix.getModuleWidth();
float moduleWidth = 1.0f; // Image has been sampled and reduced
int[] rowCounters = new int[width];
int[] codewords = new int[MAX_CW_CAPACITY];
int next = 0;
int matchingConsecutiveScans = 0;
bool rowInProgress = false;
int rowNumber = 0;
int rowHeight = 0;
for (int i = 1; i < height; i++)
{
if (rowNumber >= MAX_ROWS)
{
// Something is wrong, since we have exceeded
// the maximum rows in the specification.
throw FormatException.FormatInstance;
}
int rowDifference = 0;
// Scan a line of modules and check the
// difference between this and the previous line
for (int j = 0; j < width; j++)
{
// Accumulate differences between this line and the
// previous line.
if (bitMatrix.get(j, i) != bitMatrix.get(j, i - 1))
{
rowDifference++;
}
}
if (rowDifference <= moduleWidth * MAX_ROW_DIFFERENCE)
{
for (int j = 0; j < width; j++)
{
// Accumulate the black pixels on this line
if (bitMatrix.get(j, i))
{
rowCounters[j]++;
}
}
// Increment the number of consecutive rows of pixels
// that are more or less the same
matchingConsecutiveScans++;
// Height of a row is a multiple of the module size in pixels
// It's supposed to be >= 3x module width, but, accept anything >= 2x
if ((matchingConsecutiveScans + 1) >= 2.0f * moduleWidth)
{
// We have some previous matches as well as a match here
// Set processing a unique row.
rowInProgress = true;
}
}
else
{
if (rowInProgress)
{
// Process Row
next = processRow(rowCounters, rowNumber, rowHeight, codewords, next);
if (next == -1)
{
// Something is wrong, since we have exceeded
// the maximum columns in the specification.
throw FormatException.FormatInstance;
}
// Reinitialize the row counters.
for (int j = 0; j < rowCounters.Length; j++)
{
rowCounters[j] = 0;
}
rowNumber++;
rowHeight = 0;
}
matchingConsecutiveScans = 0;
rowInProgress = false;
}
rowHeight++;
}
// Check for a row that was in progress before we exited above.
if (rowInProgress)
{
// Process Row
if (rowNumber >= MAX_ROWS)
{
// Something is wrong, since we have exceeded
// the maximum rows in the specification.
throw FormatException.FormatInstance;
}
next = processRow(rowCounters, rowNumber, rowHeight, codewords, next);
rowNumber++;
rows = rowNumber;
}
erasures = trimArray(erasures, eraseCount);
return trimArray(codewords, next);
}
/// <summary>
/// Trim the array to the required size.
/// </summary>
/// <param name="array"> the array </param>
/// <param name="size"> the size to trim it to </param>
/// <returns> the new trimmed array </returns>
private static int[] trimArray(int[] array, int size)
{
if (size < 0)
{
throw new System.ArgumentException();
}
if (size == 0)
{
return NO_ERRORS;
}
int[] a = new int[size];
Array.Copy(array, 0, a, 0, size);
return a;
}
/// <summary>
/// Convert the symbols in the row to codewords.
/// Each PDF417 symbol character consists of four bar elements and four space
/// elements, each of which can be one to six modules wide. The four bar and
/// four space elements shall measure 17 modules in total.
/// </summary>
/// <param name="rowCounters"> an array containing the counts of black pixels for each column
/// in the row. </param>
/// <param name="rowNumber"> the current row number of codewords. </param>
/// <param name="rowHeight"> the height of this row in pixels. </param>
/// <param name="codewords"> the codeword array to save codewords into. </param>
/// <param name="next"> the next available index into the codewords array. </param>
/// <returns> the next available index into the codeword array after processing
/// this row. </returns>
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: int processRow(int[] rowCounters, int rowNumber, int rowHeight, int[] codewords, int next) throws com.google.zxing.FormatException
internal int processRow(int[] rowCounters, int rowNumber, int rowHeight, int[] codewords, int next)
{
int width = bitMatrix.Width;
int columnNumber = 0;
long symbol = 0;
for (int i = 0; i < width; i += MODULES_IN_SYMBOL)
{
// This happens in real life and is almost surely a rare misdecode
if (i + MODULES_IN_SYMBOL > rowCounters.Length)
{
throw FormatException.FormatInstance;
}
for (int mask = MODULES_IN_SYMBOL - 1; mask >= 0; mask--)
{
if (rowCounters[i + (MODULES_IN_SYMBOL - 1 - mask)] >= (int)((uint)rowHeight >> 1))
{
symbol |= 1L << mask;
}
}
if (columnNumber > 0)
{
int cw = getCodeword(symbol);
if (cw < 0 && i < width - MODULES_IN_SYMBOL)
{
// Skip errors on the Right row indicator column
if (eraseCount >= erasures.Length)
{
throw FormatException.FormatInstance;
}
erasures[eraseCount] = next;
next++;
eraseCount++;
}
else
{
codewords[next++] = cw;
}
}
else
{
// Left row indicator column
int cw = getCodeword(symbol);
if (ecLevel < 0 && rowNumber % 3 == 1)
{
leftColumnECData = cw;
}
}
symbol = 0;
//columns = columnNumber;
columnNumber++;
}
if (columnNumber > 1)
{
// Right row indicator column is in codeword[next]
//columns--;
// Overwrite the last codeword i.e. Right Row Indicator
--next;
if (ecLevel < 0 && rowNumber % 3 == 2)
{
rightColumnECData = codewords[next];
if (rightColumnECData == leftColumnECData && leftColumnECData != 0)
{
ecLevel = ((rightColumnECData % 30) - rows % 3) / 3;
}
}
codewords[next] = 0;
}
return next;
}
/// <summary>
/// Translate the symbol into a codeword.
/// </summary>
/// <returns> the codeword corresponding to the symbol. </returns>
private static int getCodeword(long symbol)
{
long sym = symbol & 0x3FFFF;
int i = findCodewordIndex(sym);
if (i == -1)
{
return -1;
}
else
{
long cw = CODEWORD_TABLE[i] - 1;
cw %= 929;
return (int) cw;
}
}
/// <summary>
/// Use a binary search to find the index of the codeword corresponding to
/// this symbol.
/// </summary>
/// <param name="symbol"> the symbol from the barcode. </param>
/// <returns> the index into the codeword table. </returns>
private static int findCodewordIndex(long symbol)
{
int first = 0;
int upto = SYMBOL_TABLE.Length;
while (first < upto)
{
int mid = (int)((uint)(first + upto) >> 1); // Compute mid point.
if (symbol < SYMBOL_TABLE[mid])
{
upto = mid; // repeat search in bottom half.
}
else if (symbol > SYMBOL_TABLE[mid])
{
first = mid + 1; // Repeat search in top half.
}
else
{
return mid; // Found it. return position
}
}
return -1;
}
/// <summary>
/// Returns an array of locations representing the erasures.
/// </summary>
public int[] Erasures
{
get
{
return erasures;
}
}
public int ECLevel
{
get
{
return ecLevel;
}
}
/// <summary>
/// The sorted table of all possible symbols. Extracted from the PDF417
/// specification. The index of a symbol in this table corresponds to the
/// index into the codeword table.
/// </summary>
private static readonly int[] SYMBOL_TABLE = {0x1025e, 0x1027a, 0x1029e, 0x102bc, 0x102f2, 0x102f4, 0x1032e, 0x1034e, 0x1035c, 0x10396, 0x103a6, 0x103ac, 0x10422, 0x10428, 0x10436, 0x10442, 0x10444, 0x10448, 0x10450, 0x1045e, 0x10466, 0x1046c, 0x1047a, 0x10482, 0x1049e, 0x104a0, 0x104bc, 0x104c6, 0x104d8, 0x104ee, 0x104f2, 0x104f4, 0x10504, 0x10508, 0x10510, 0x1051e, 0x10520, 0x1053c, 0x10540, 0x10578, 0x10586, 0x1058c, 0x10598, 0x105b0, 0x105be, 0x105ce, 0x105dc, 0x105e2, 0x105e4, 0x105e8, 0x105f6, 0x1062e, 0x1064e, 0x1065c, 0x1068e, 0x1069c, 0x106b8, 0x106de, 0x106fa, 0x10716, 0x10726, 0x1072c, 0x10746, 0x1074c, 0x10758, 0x1076e, 0x10792, 0x10794, 0x107a2, 0x107a4, 0x107a8, 0x107b6, 0x10822, 0x10828, 0x10842, 0x10848, 0x10850, 0x1085e, 0x10866, 0x1086c, 0x1087a, 0x10882, 0x10884, 0x10890, 0x1089e, 0x108a0, 0x108bc, 0x108c6, 0x108cc, 0x108d8, 0x108ee, 0x108f2, 0x108f4, 0x10902, 0x10908, 0x1091e, 0x10920, 0x1093c, 0x10940, 0x10978, 0x10986, 0x10998, 0x109b0, 0x109be, 0x109ce, 0x109dc, 0x109e2, 0x109e4, 0x109e8, 0x109f6, 0x10a08, 0x10a10, 0x10a1e, 0x10a20, 0x10a3c, 0x10a40, 0x10a78, 0x10af0, 0x10b06, 0x10b0c, 0x10b18, 0x10b30, 0x10b3e, 0x10b60, 0x10b7c, 0x10b8e, 0x10b9c, 0x10bb8, 0x10bc2, 0x10bc4, 0x10bc8, 0x10bd0, 0x10bde, 0x10be6, 0x10bec, 0x10c2e, 0x10c4e, 0x10c5c, 0x10c62, 0x10c64, 0x10c68, 0x10c76, 0x10c8e, 0x10c9c, 0x10cb8, 0x10cc2, 0x10cc4, 0x10cc8, 0x10cd0, 0x10cde, 0x10ce6, 0x10cec, 0x10cfa, 0x10d0e, 0x10d1c, 0x10d38, 0x10d70, 0x10d7e, 0x10d82, 0x10d84, 0x10d88, 0x10d90, 0x10d9e, 0x10da0, 0x10dbc, 0x10dc6, 0x10dcc, 0x10dd8, 0x10dee, 0x10df2, 0x10df4, 0x10e16, 0x10e26, 0x10e2c, 0x10e46, 0x10e58, 0x10e6e, 0x10e86, 0x10e8c, 0x10e98, 0x10eb0, 0x10ebe, 0x10ece, 0x10edc, 0x10f0a, 0x10f12, 0x10f14, 0x10f22, 0x10f28, 0x10f36, 0x10f42, 0x10f44, 0x10f48, 0x10f50, 0x10f5e, 0x10f66, 0x10f6c, 0x10fb2, 0x10fb4, 0x11022, 0x11028, 0x11042, 0x11048, 0x11050, 0x1105e, 0x1107a, 0x11082, 0x11084, 0x11090, 0x1109e, 0x110a0, 0x110bc, 0x110c6, 0x110cc, 0x110d8, 0x110ee, 0x110f2, 0x110f4, 0x11102, 0x1111e, 0x11120, 0x1113c, 0x11140, 0x11178, 0x11186, 0x11198, 0x111b0, 0x111be, 0x111ce, 0x111dc, 0x111e2, 0x111e4, 0x111e8, 0x111f6, 0x11208, 0x1121e, 0x11220, 0x11278, 0x112f0, 0x1130c, 0x11330, 0x1133e, 0x11360, 0x1137c, 0x1138e, 0x1139c, 0x113b8, 0x113c2, 0x113c8, 0x113d0, 0x113de, 0x113e6, 0x113ec, 0x11408, 0x11410, 0x1141e, 0x11420, 0x1143c, 0x11440, 0x11478, 0x114f0, 0x115e0, 0x1160c, 0x11618, 0x11630, 0x1163e, 0x11660, 0x1167c, 0x116c0, 0x116f8, 0x1171c, 0x11738, 0x11770, 0x1177e, 0x11782, 0x11784, 0x11788, 0x11790, 0x1179e, 0x117a0, 0x117bc, 0x117c6, 0x117cc, 0x117d8, 0x117ee, 0x1182e, 0x11834, 0x1184e, 0x1185c, 0x11862, 0x11864, 0x11868, 0x11876, 0x1188e, 0x1189c, 0x118b8, 0x118c2, 0x118c8, 0x118d0, 0x118de, 0x118e6, 0x118ec, 0x118fa, 0x1190e, 0x1191c, 0x11938, 0x11970, 0x1197e, 0x11982, 0x11984, 0x11990, 0x1199e, 0x119a0, 0x119bc, 0x119c6, 0x119cc, 0x119d8, 0x119ee, 0x119f2, 0x119f4, 0x11a0e, 0x11a1c, 0x11a38, 0x11a70, 0x11a7e, 0x11ae0, 0x11afc, 0x11b08, 0x11b10, 0x11b1e, 0x11b20, 0x11b3c, 0x11b40, 0x11b78, 0x11b8c, 0x11b98, 0x11bb0, 0x11bbe, 0x11bce, 0x11bdc, 0x11be2, 0x11be4, 0x11be8, 0x11bf6, 0x11c16, 0x11c26, 0x11c2c, 0x11c46, 0x11c4c, 0x11c58, 0x11c6e, 0x11c86, 0x11c98, 0x11cb0, 0x11cbe, 0x11cce, 0x11cdc, 0x11ce2, 0x11ce4, 0x11ce8, 0x11cf6, 0x11d06, 0x11d0c, 0x11d18, 0x11d30, 0x11d3e, 0x11d60, 0x11d7c, 0x11d8e, 0x11d9c, 0x11db8, 0x11dc4, 0x11dc8, 0x11dd0, 0x11dde, 0x11de6, 0x11dec, 0x11dfa, 0x11e0a, 0x11e12, 0x11e14, 0x11e22, 0x11e24, 0x11e28, 0x11e36, 0x11e42, 0x11e44, 0x11e50, 0x11e5e, 0x11e66, 0x11e6c, 0x11e82, 0x11e84, 0x11e88, 0x11e90, 0x11e9e, 0x11ea0, 0x11ebc, 0x11ec6, 0x11ecc, 0x11ed8, 0x11eee, 0x11f1a, 0x11f2e, 0x11f32, 0x11f34, 0x11f4e, 0x11f5c, 0x11f62, 0x11f64, 0x11f68, 0x11f76, 0x12048, 0x1205e, 0x12082, 0x12084, 0x12090, 0x1209e, 0x120a0, 0x120bc, 0x120d8, 0x120f2, 0x120f4, 0x12108, 0x1211e, 0x12120, 0x1213c, 0x12140, 0x12178, 0x12186, 0x12198, 0x121b0, 0x121be, 0x121e2, 0x121e4, 0x121e8, 0x121f6, 0x12204, 0x12210, 0x1221e, 0x12220, 0x12278, 0x122f0, 0x12306, 0x1230c, 0x12330, 0x1233e, 0x12360, 0x1237c, 0x123
/// <summary>
/// This table contains to codewords for all symbols.
/// </summary>
private static readonly int[] CODEWORD_TABLE = {2627, 1819, 2622, 2621, 1813, 1812, 2729, 2724, 2723, 2779, 2774, 2773, 902, 896, 908, 868, 865, 861, 859, 2511, 873, 871, 1780, 835, 2493, 825, 2491, 842, 837, 844, 1764, 1762, 811, 810, 809, 2483, 807, 2482, 806, 2480, 815, 814, 813, 812, 2484, 817, 816, 1745, 1744, 1742, 1746, 2655, 2637, 2635, 2626, 2625, 2623, 2628, 1820, 2752, 2739, 2737, 2728, 2727, 2725, 2730, 2785, 2783, 2778, 2777, 2775, 2780, 787, 781, 747, 739, 736, 2413, 754, 752, 1719, 692, 689, 681, 2371, 678, 2369, 700, 697, 694, 703, 1688, 1686, 642, 638, 2343, 631, 2341, 627, 2338, 651, 646, 643, 2345, 654, 652, 1652, 1650, 1647, 1654, 601, 599, 2322, 596, 2321, 594, 2319, 2317, 611, 610, 608, 606, 2324, 603, 2323, 615, 614, 612, 1617, 1616, 1614, 1612, 616, 1619, 1618, 2575, 2538, 2536, 905, 901, 898, 909, 2509, 2507, 2504, 870, 867, 864, 860, 2512, 875, 872, 1781, 2490, 2489, 2487, 2485, 1748, 836, 834, 832, 830, 2494, 827, 2492, 843, 841, 839, 845, 1765, 1763, 2701, 2676, 2674, 2653, 2648, 2656, 2634, 2633, 2631, 2629, 1821, 2638, 2636, 2770, 2763, 2761, 2750, 2745, 2753, 2736, 2735, 2733, 2731, 1848, 2740, 2738, 2786, 2784, 591, 588, 576, 569, 566, 2296, 1590, 537, 534, 526, 2276, 522, 2274, 545, 542, 539, 548, 1572, 1570, 481, 2245, 466, 2242, 462, 2239, 492, 485, 482, 2249, 496, 494, 1534, 1531, 1528, 1538, 413, 2196, 406, 2191, 2188, 425, 419, 2202, 415, 2199, 432, 430, 427, 1472, 1467, 1464, 433, 1476, 1474, 368, 367, 2160, 365, 2159, 362, 2157, 2155, 2152, 378, 377, 375, 2166, 372, 2165, 369, 2162, 383, 381, 379, 2168, 1419, 1418, 1416, 1414, 385, 1411, 384, 1423, 1422, 1420, 1424, 2461, 802, 2441, 2439, 790, 786, 783, 794, 2409, 2406, 2403, 750, 742, 738, 2414, 756, 753, 1720, 2367, 2365, 2362, 2359, 1663, 693, 691, 684, 2373, 680, 2370, 702, 699, 696, 704, 1690, 1687, 2337, 2336, 2334, 2332, 1624, 2329, 1622, 640, 637, 2344, 634, 2342, 630, 2340, 650, 648, 645, 2346, 655, 653, 1653, 1651, 1649, 1655, 2612, 2597, 2595, 2571, 2568, 2565, 2576, 2534, 2529, 2526, 1787, 2540, 2537, 907, 904, 900, 910, 2503, 2502, 2500, 2498, 1768, 2495, 1767, 2510, 2508, 2506, 869, 866, 863, 2513, 876, 874, 1782, 2720, 2713, 2711, 2697, 2694, 2691, 2702, 2672, 2670, 2664, 1828, 2678, 2675, 2647, 2646, 2644, 2642, 1823, 2639, 1822, 2654, 2652, 2650, 2657, 2771, 1855, 2765, 2762, 1850, 1849, 2751, 2749, 2747, 2754, 353, 2148, 344, 342, 336, 2142, 332, 2140, 345, 1375, 1373, 306, 2130, 299, 2128, 295, 2125, 319, 314, 311, 2132, 1354, 1352, 1349, 1356, 262, 257, 2101, 253, 2096, 2093, 274, 273, 267, 2107, 263, 2104, 280, 278, 275, 1316, 1311, 1308, 1320, 1318, 2052, 202, 2050, 2044, 2040, 219, 2063, 212, 2060, 208, 2055, 224, 221, 2066, 1260, 1258, 1252, 231, 1248, 229, 1266, 1264, 1261, 1268, 155, 1998, 153, 1996, 1994, 1991, 1988, 165, 164, 2007, 162, 2006, 159, 2003, 2000, 172, 171, 169, 2012, 166, 2010, 1186, 1184, 1182, 1179, 175, 1176, 173, 1192, 1191, 1189, 1187, 176, 1194, 1193, 2313, 2307, 2305, 592, 589, 2294, 2292, 2289, 578, 572, 568, 2297, 580, 1591, 2272, 2267, 2264, 1547, 538, 536, 529, 2278, 525, 2275, 547, 544, 541, 1574, 1571, 2237, 2235, 2229, 1493, 2225, 1489, 478, 2247, 470, 2244, 465, 2241, 493, 488, 484, 2250, 498, 495, 1536, 1533, 1530, 1539, 2187, 2186, 2184, 2182, 1432, 2179, 1430, 2176, 1427, 414, 412, 2197, 409, 2195, 405, 2193, 2190, 426, 424, 421, 2203, 418, 2201, 431, 429, 1473, 1471, 1469, 1466, 434, 1477, 1475, 2478, 2472, 2470, 2459, 2457, 2454, 2462, 803, 2437, 2432, 2429, 1726, 2443, 2440, 792, 789, 785, 2401, 2399, 2393, 1702, 2389, 1699, 2411, 2408, 2405, 745, 741, 2415, 758, 755, 1721, 2358, 2357, 2355, 2353, 1661, 2350, 1660, 2347, 1657, 2368, 2366, 2364, 2361, 1666, 690, 687, 2374, 683, 2372, 701, 698, 705, 1691, 1689, 2619, 2617, 2610, 2608, 2605, 2613, 2593, 2588, 2585, 1803, 2599, 2596, 2563, 2561, 2555, 1797, 2551, 1795, 2573, 2570, 2567, 2577, 2525, 2524, 2522, 2520, 1786, 2517, 1785, 2514, 1783, 2535, 2533, 2531, 2528, 1788, 2541, 2539, 906, 903, 911, 2721, 1844, 2715, 2712, 1838, 1836, 2699, 2696, 2693, 2703, 1827, 1826, 1824, 2673, 2671, 2669, 2666, 1829, 2679, 2677, 1
}
}