private static void testHighLevelEncodeString(String s, int expectedReceivedBits) { BitArray bits = new HighLevelEncoder(LATIN_1.GetBytes(s)).encode(); int receivedBitCount = bits.ToString().Replace(" ", "").Length; Assert.AreEqual(expectedReceivedBits, receivedBitCount, "highLevelEncode() failed for input string: " + s); Assert.AreEqual(s, Internal.Decoder.highLevelDecode(toBooleanArray(bits))); }
/// <summary> /// Encodes the given binary content as an Aztec symbol /// </summary> /// <param name="data">input data string</param> /// <param name="minECCPercent">minimal percentage of error check words (According to ISO/IEC 24778:2008, /// a minimum of 23% + 3 words is recommended)</param> /// <param name="userSpecifiedLayers">if non-zero, a user-specified value for the number of layers</param> /// <returns> /// Aztec symbol matrix with metadata /// </returns> public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers) { // High-level encode var bits = new HighLevelEncoder(data).encode(); // stuff bits and choose symbol size int eccBits = bits.Size*minECCPercent/100 + 11; int totalSizeBits = bits.Size + eccBits; bool compact; int layers; int totalBitsInLayer; int wordSize; BitArray stuffedBits; if (userSpecifiedLayers != DEFAULT_AZTEC_LAYERS) { compact = userSpecifiedLayers < 0; layers = Math.Abs(userSpecifiedLayers); if (layers > (compact ? MAX_NB_BITS_COMPACT : MAX_NB_BITS)) { throw new ArgumentException( String.Format("Illegal value {0} for layers", userSpecifiedLayers)); } totalBitsInLayer = TotalBitsInLayer(layers, compact); wordSize = WORD_SIZE[layers]; int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer%wordSize); stuffedBits = stuffBits(bits, wordSize); if (stuffedBits.Size + eccBits > usableBitsInLayers) { throw new ArgumentException("Data to large for user specified layer"); } if (compact && stuffedBits.Size > wordSize*64) { // Compact format only allows 64 data words, though C4 can hold more words than that throw new ArgumentException("Data to large for user specified layer"); } } else { wordSize = 0; stuffedBits = null; // We look at the possible table sizes in the order Compact1, Compact2, Compact3, // Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1) // is the same size, but has more data. for (int i = 0;; i++) { if (i > MAX_NB_BITS) { throw new ArgumentException("Data too large for an Aztec code"); } compact = i <= 3; layers = compact ? i + 1 : i; totalBitsInLayer = TotalBitsInLayer(layers, compact); if (totalSizeBits > totalBitsInLayer) { continue; } // [Re]stuff the bits if this is the first opportunity, or if the // wordSize has changed if (wordSize != WORD_SIZE[layers]) { wordSize = WORD_SIZE[layers]; stuffedBits = stuffBits(bits, wordSize); } if (stuffedBits == null) { continue; } int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer%wordSize); if (compact && stuffedBits.Size > wordSize*64) { // Compact format only allows 64 data words, though C4 can hold more words than that continue; } if (stuffedBits.Size + eccBits <= usableBitsInLayers) { break; } } } BitArray messageBits = generateCheckWords(stuffedBits, totalBitsInLayer, wordSize); // generate mode message int messageSizeInWords = stuffedBits.Size / wordSize; var modeMessage = generateModeMessage(compact, layers, messageSizeInWords); // allocate symbol var baseMatrixSize = compact ? 11 + layers*4 : 14 + layers*4; // not including alignment lines var alignmentMap = new int[baseMatrixSize]; int matrixSize; if (compact) { // no alignment marks in compact mode, alignmentMap is a no-op matrixSize = baseMatrixSize; for (int i = 0; i < alignmentMap.Length; i++) { alignmentMap[i] = i; } } else { matrixSize = baseMatrixSize + 1 + 2*((baseMatrixSize/2 - 1)/15); int origCenter = baseMatrixSize/2; int center = matrixSize/2; for (int i = 0; i < origCenter; i++) { int newOffset = i + i/15; alignmentMap[origCenter - i - 1] = center - newOffset - 1; alignmentMap[origCenter + i] = center + newOffset + 1; } } var matrix = new BitMatrix(matrixSize); // draw data bits for (int i = 0, rowOffset = 0; i < layers; i++) { int rowSize = compact ? (layers - i)*4 + 9 : (layers - i)*4 + 12; for (int j = 0; j < rowSize; j++) { int columnOffset = j*2; for (int k = 0; k < 2; k++) { if (messageBits[rowOffset + columnOffset + k]) { matrix[alignmentMap[i*2 + k], alignmentMap[i*2 + j]] = true; } if (messageBits[rowOffset + rowSize*2 + columnOffset + k]) { matrix[alignmentMap[i*2 + j], alignmentMap[baseMatrixSize - 1 - i*2 - k]] = true; } if (messageBits[rowOffset + rowSize*4 + columnOffset + k]) { matrix[alignmentMap[baseMatrixSize - 1 - i*2 - k], alignmentMap[baseMatrixSize - 1 - i*2 - j]] = true; } if (messageBits[rowOffset + rowSize*6 + columnOffset + k]) { matrix[alignmentMap[baseMatrixSize - 1 - i*2 - j], alignmentMap[i*2 + k]] = true; } } } rowOffset += rowSize*8; } // draw mode message drawModeMessage(matrix, compact, matrixSize, modeMessage); // draw alignment marks if (compact) { drawBullsEye(matrix, matrixSize/2, 5); } else { drawBullsEye(matrix, matrixSize/2, 7); for (int i = 0, j = 0; i < baseMatrixSize/2 - 1; i += 15, j += 16) { for (int k = (matrixSize/2) & 1; k < matrixSize; k += 2) { matrix[matrixSize/2 - j, k] = true; matrix[matrixSize/2 + j, k] = true; matrix[k, matrixSize/2 - j] = true; matrix[k, matrixSize/2 + j] = true; } } } return new AztecCode { isCompact = compact, Size = matrixSize, Layers = layers, CodeWords = messageSizeInWords, Matrix = matrix }; }
/// <summary> /// Encodes the given binary content as an Aztec symbol /// </summary> /// <param name="data">input data string</param> /// <param name="minECCPercent">minimal percentage of error check words (According to ISO/IEC 24778:2008, a minimum of 23% + 3 words is recommended)</param> /// <param name="userSpecifiedLayers">if non-zero, a user-specified value for the number of layers</param> /// <param name="encoding">character set to mark using ECI; if null, no ECI code will be inserted, and the default encoding of ISO/IEC 8859-1 will be assuming by readers.</param> /// <param name="disableEci">if true, don't add ECI segment, regardless if encoding ist set</param> /// <returns>Aztec symbol matrix with metadata</returns> public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers, System.Text.Encoding encoding, bool disableEci) { // High-level encode BitArray bits = new HighLevelEncoder(data, encoding, disableEci).encode(); // stuff bits and choose symbol size int eccBits = bits.Size * minECCPercent / 100 + 11; int totalSizeBits = bits.Size + eccBits; bool compact; int layers; int totalBitsInLayer; int wordSize; BitArray stuffedBits; if (userSpecifiedLayers != DEFAULT_AZTEC_LAYERS) { compact = userSpecifiedLayers < 0; layers = Math.Abs(userSpecifiedLayers); if (layers > (compact ? MAX_NB_BITS_COMPACT : MAX_NB_BITS)) { throw new ArgumentException( String.Format("Illegal value {0} for layers", userSpecifiedLayers)); } totalBitsInLayer = TotalBitsInLayer(layers, compact); wordSize = WORD_SIZE[layers]; int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize); stuffedBits = stuffBits(bits, wordSize); if (stuffedBits.Size + eccBits > usableBitsInLayers) { throw new ArgumentException("Data to large for user specified layer"); } if (compact && stuffedBits.Size > wordSize * 64) { // Compact format only allows 64 data words, though C4 can hold more words than that throw new ArgumentException("Data to large for user specified layer"); } } else { wordSize = 0; stuffedBits = null; // We look at the possible table sizes in the order Compact1, Compact2, Compact3, // Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1) // is the same size, but has more data. for (int i = 0; ; i++) { if (i > MAX_NB_BITS) { throw new ArgumentException("Data too large for an Aztec code"); } compact = i <= 3; layers = compact ? i + 1 : i; totalBitsInLayer = TotalBitsInLayer(layers, compact); if (totalSizeBits > totalBitsInLayer) { continue; } // [Re]stuff the bits if this is the first opportunity, or if the // wordSize has changed if (stuffedBits == null || wordSize != WORD_SIZE[layers]) { wordSize = WORD_SIZE[layers]; stuffedBits = stuffBits(bits, wordSize); } int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize); if (compact && stuffedBits.Size > wordSize * 64) { // Compact format only allows 64 data words, though C4 can hold more words than that continue; } if (stuffedBits.Size + eccBits <= usableBitsInLayers) { break; } } } BitArray messageBits = generateCheckWords(stuffedBits, totalBitsInLayer, wordSize); // generate mode message int messageSizeInWords = stuffedBits.Size / wordSize; var modeMessage = generateModeMessage(compact, layers, messageSizeInWords); // allocate symbol int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines var alignmentMap = new int[baseMatrixSize]; int matrixSize; if (compact) { // no alignment marks in compact mode, alignmentMap is a no-op matrixSize = baseMatrixSize; for (int i = 0; i < alignmentMap.Length; i++) { alignmentMap[i] = i; } } else { matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15); int origCenter = baseMatrixSize / 2; int center = matrixSize / 2; for (int i = 0; i < origCenter; i++) { int newOffset = i + i / 15; alignmentMap[origCenter - i - 1] = center - newOffset - 1; alignmentMap[origCenter + i] = center + newOffset + 1; } } var matrix = new BitMatrix(matrixSize); // draw data bits for (int i = 0, rowOffset = 0; i < layers; i++) { int rowSize = (layers - i) * 4 + (compact ? 9 : 12); for (int j = 0; j < rowSize; j++) { int columnOffset = j * 2; for (int k = 0; k < 2; k++) { if (messageBits[rowOffset + columnOffset + k]) { matrix[alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]] = true; } if (messageBits[rowOffset + rowSize * 2 + columnOffset + k]) { matrix[alignmentMap[i * 2 + j], alignmentMap[baseMatrixSize - 1 - i * 2 - k]] = true; } if (messageBits[rowOffset + rowSize * 4 + columnOffset + k]) { matrix[alignmentMap[baseMatrixSize - 1 - i * 2 - k], alignmentMap[baseMatrixSize - 1 - i * 2 - j]] = true; } if (messageBits[rowOffset + rowSize * 6 + columnOffset + k]) { matrix[alignmentMap[baseMatrixSize - 1 - i * 2 - j], alignmentMap[i * 2 + k]] = true; } } } rowOffset += rowSize * 8; } // draw mode message drawModeMessage(matrix, compact, matrixSize, modeMessage); // draw alignment marks if (compact) { drawBullsEye(matrix, matrixSize / 2, 5); } else { drawBullsEye(matrix, matrixSize / 2, 7); for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) { for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) { matrix[matrixSize / 2 - j, k] = true; matrix[matrixSize / 2 + j, k] = true; matrix[k, matrixSize / 2 - j] = true; matrix[k, matrixSize / 2 + j] = true; } } } return(new AztecCode { isCompact = compact, Size = matrixSize, Layers = layers, CodeWords = messageSizeInWords, Matrix = matrix }); }
/// <summary> /// Encodes the given binary content as an Aztec symbol /// </summary> /// <param name="data">input data string</param> /// <param name="minECCPercent">minimal percentange of error check words (According to ISO/IEC 24778:2008, /// a minimum of 23% + 3 words is recommended)</param> /// <returns>Aztec symbol matrix with metadata</returns> public static AztecCode encode(byte[] data, int minECCPercent) { // High-level encode var bits = new HighLevelEncoder(data).encode(); // stuff bits and choose symbol size int eccBits = bits.Size * minECCPercent / 100 + 11; int totalSizeBits = bits.Size + eccBits; int layers; int wordSize = 0; int totalSymbolBits = 0; BitArray stuffedBits = null; for (layers = 1; layers < NB_BITS_COMPACT.Length; layers++) { if (NB_BITS_COMPACT[layers] >= totalSizeBits) { if (wordSize != WORD_SIZE[layers]) { wordSize = WORD_SIZE[layers]; stuffedBits = stuffBits(bits, wordSize); } totalSymbolBits = NB_BITS_COMPACT[layers]; if (stuffedBits.Size + eccBits <= NB_BITS_COMPACT[layers]) { break; } } } bool compact = true; if (layers == NB_BITS_COMPACT.Length) { compact = false; for (layers = 1; layers < NB_BITS.Length; layers++) { if (NB_BITS[layers] >= totalSizeBits) { if (wordSize != WORD_SIZE[layers]) { wordSize = WORD_SIZE[layers]; stuffedBits = stuffBits(bits, wordSize); } totalSymbolBits = NB_BITS[layers]; if (stuffedBits.Size + eccBits <= NB_BITS[layers]) { break; } } } } if (layers == NB_BITS.Length) { throw new ArgumentException("Data too large for an Aztec code"); } // pad the end int messageSizeInWords = (stuffedBits.Size + wordSize - 1) / wordSize; for (int i = messageSizeInWords * wordSize - stuffedBits.Size; i > 0; i--) { stuffedBits.appendBit(true); } // generate check words var rs = new ReedSolomonEncoder(getGF(wordSize)); var totalSizeInFullWords = totalSymbolBits / wordSize; var messageWords = bitsToWords(stuffedBits, wordSize, totalSizeInFullWords); rs.encode(messageWords, totalSizeInFullWords - messageSizeInWords); // convert to bit array and pad in the beginning var startPad = totalSymbolBits % wordSize; var messageBits = new BitArray(); messageBits.appendBits(0, startPad); foreach (var messageWord in messageWords) { messageBits.appendBits(messageWord, wordSize); } // generate mode message var modeMessage = generateModeMessage(compact, layers, messageSizeInWords); // allocate symbol var baseMatrixSize = compact ? 11 + layers * 4 : 14 + layers * 4; // not including alignment lines var alignmentMap = new int[baseMatrixSize]; int matrixSize; if (compact) { // no alignment marks in compact mode, alignmentMap is a no-op matrixSize = baseMatrixSize; for (int i = 0; i < alignmentMap.Length; i++) { alignmentMap[i] = i; } } else { matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15); int origCenter = baseMatrixSize / 2; int center = matrixSize / 2; for (int i = 0; i < origCenter; i++) { int newOffset = i + i / 15; alignmentMap[origCenter - i - 1] = center - newOffset - 1; alignmentMap[origCenter + i] = center + newOffset + 1; } } var matrix = new BitMatrix(matrixSize); // draw mode and data bits for (int i = 0, rowOffset = 0; i < layers; i++) { int rowSize = compact ? (layers - i) * 4 + 9 : (layers - i) * 4 + 12; for (int j = 0; j < rowSize; j++) { int columnOffset = j * 2; for (int k = 0; k < 2; k++) { if (messageBits[rowOffset + columnOffset + k]) { matrix[alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]] = true; } if (messageBits[rowOffset + rowSize * 2 + columnOffset + k]) { matrix[alignmentMap[i * 2 + j], alignmentMap[baseMatrixSize - 1 - i * 2 - k]] = true; } if (messageBits[rowOffset + rowSize * 4 + columnOffset + k]) { matrix[alignmentMap[baseMatrixSize - 1 - i * 2 - k], alignmentMap[baseMatrixSize - 1 - i * 2 - j]] = true; } if (messageBits[rowOffset + rowSize * 6 + columnOffset + k]) { matrix[alignmentMap[baseMatrixSize - 1 - i * 2 - j], alignmentMap[i * 2 + k]] = true; } } } rowOffset += rowSize * 8; } drawModeMessage(matrix, compact, matrixSize, modeMessage); // draw alignment marks if (compact) { drawBullsEye(matrix, matrixSize / 2, 5); } else { drawBullsEye(matrix, matrixSize / 2, 7); for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) { for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) { matrix[matrixSize / 2 - j, k] = true; matrix[matrixSize / 2 + j, k] = true; matrix[k, matrixSize / 2 - j] = true; matrix[k, matrixSize / 2 + j] = true; } } } return new AztecCode { isCompact = compact, Size = matrixSize, Layers = layers, CodeWords = messageSizeInWords, Matrix = matrix }; }