通过AVMetadataMachineReadableCodeObject从Aztec条形码读取原始字节会产生意外的结果

我一直在研究一种适用于德国铁路公司(德国铁路公司)使用的特殊条形码的iOS阅读器应用程序。 它是一个连接DSA签名string和zlib瘪负载的Aztec条形码的变体。

当我得知AVMetadataMachineReadableCodeObject没有读取条码中包含的原始字节的公开方法时,我被卡住了,string方法总是乱码zlib压缩数据。

幸运的是, 这个答案给了我正确的方向。 (私人)字节可以通过KVO访问,因为我目前不期望在App Store上分发应用程序,所以这是完美的。

尽pipe我几乎不存在SwiftObjective-C知识,但是我设法使这个工作起来,就像你在示例代码中看到的那样。 但是,存储在NSData的条形码字节与预期的结果不符! 我怀疑我使用的zlib库( DeflateSwift )没有工作,所以我构build了一个testing用例,它工作正常。

我的问题是:我做错了什么? 我是否需要进一步处理原始字节以获得预期结果(请参阅下文)? AVMetadataMachineReadableCodeObject中存储的字节的原始数据是AVMetadataMachineReadableCodeObject ? 任何人都可以指向正确的方向吗? 任何帮助表示赞赏。

这是我的代码(这是一个SwiftObjective-C混搭)

 if let metadataObject = metadataObjects.first { let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject; let rawReadableObject = readableObject.valueForKeyPath("_internal.basicDescriptor")!["BarcodeRawData"] as? NSData; if let rawBytes = rawReadableObject { let barcodeData = rawBytes; // or use testData instead let barcodeSplit:Int = 68; let barcodeLength:Int = barcodeData.length; let barcodeHeader:NSData = barcodeData.subdataWithRange(NSRange(location: 0, length: barcodeSplit)) let barcodeZlibContent:NSData = barcodeData.subdataWithRange(NSRange(location: barcodeSplit, length: (barcodeLength-barcodeSplit))) let count = barcodeZlibContent.length / sizeof(UInt8) var array = [UInt8](count: count, repeatedValue: 0) barcodeZlibContent.getBytes(&array, length:count * sizeof(UInt8)) print("\(barcodeLength)kb") print(barcodeHeader) print(barcodeZlibContent) var inflater = InflateStream() var (inflated, err) = inflater.write(array, flush: true) if err != nil{ fatalError("\(err!)") } if let ticketString = String(bytes: inflated, encoding: NSUTF8StringEncoding) { print(ticketString) } else { print("not a valid UTF-8 sequence") } } } 

这是我得到的

AVMetadataMachineReadableCodeObject返回的字节是

40 B4 FA 88 89 8A 88 88 98 E6 3E 20 09 10 0A 0E EF 25 ED AC DE C8 80 5A 6F 9D 21 9F 4A 6D 61 33 19 F3 12 10 8A 80 2B F0 C2 7C CE E0 AB 83 46 AF A6 42 79 FD E8 35 D4 8B 0B 00 00 00 00 03 13 3B A3 47 8E A9 C2 B4 DC 30 03 C2 89 32 8D A3 B0 D4 E6 2B 35 5B 7B 08 88 12 A0 AA A2 00 8E 22 20 31 95 10 1C 21 2A FF 78 2C BE 31 1B A2 12 B5 CF A3 87 9B 9B 59 EF 7B BC AC AE CA 88 C8 1C 02 E8 D2 B5 87 76 0D 93 77 8B FB 04 A2 B5 D1 F8 9A 67 D5 55 15 DA 61 13 91 EC 08 60 2D 9B 86 E1 94 35 C3 D8 A9 49 41 5B 3A 7C 59 A5 FD 9A E3 FE F8 3C 9F 3F 7B B2 59 DC 98 E3 5E 92 CC C0 21 11 EC AF BA D7 F4 5D DB FC BD A5 CA AF 99 08 28 E3 02 30 06 20 A8 00 88 43 8E A2 58 2D 87 24 33 40 18 C1 AE 50 04 08 91 7E 59 E1 F6 9B 87 E7 8A 67 AA 1B 3E FF FE EF 79 46 18 5A 23 03 B4 E9 1A 4F 2F 15 EA DC 46 F5 A9 67 AE B8 F7 16 0B F2 38 8B B3 96 35 34 AB D3 A6 0E 6C 77 9D 72 D5 85 7E 58 0B E0 25 69 2C AC 42 9C 13 0F 27 4F 13 72 4A 90 CB 1C ED 78 B3 60 F4 AD 4C FE 2B F4 51 A8 0D 60 CC DF 78 C7 65 78 CC E6 63 02 45 B1 F3 1F A8 ED 9E FE 63 00 00 00 00

这是我用来testing的Deutsche Bahn票据的样本条形码。 样品德国铁路阿兹台克条形码

这是我需要的

当用适当的条形码阅读器(我使用bcTester 5)进行扫描时,会产生以下字节:

23 55 54 30 31 30 30 38 30 30 30 30 30 31 30 2C 02 14 1C 3D E9 2D CD 5E C4 C0 56 BD AE 61 3E 54 AD A1 B3 26 33 D2 02 14 40 75 03 D0 CF 9C C1 F5 70 58 BD 59 50 A7 AF C5 EB 0A F4 74 00 00 00 00 30 32 37 31 78 9C 65 50 CB 4E C3 30 10 E4 53 2C 71 43 4A D9 F5 2B 36 B7 84 04 52 01 55 51 40 1C 51 01 23 2A 42 0E 21 15 3F C7 8D 1F 63 36 11 52 2B 7C F1 78 76 76 66 BD F7 8F 4D 5D 54 C4 44 CE 10 05 D2 EB 78 5B AC 32 7B B4 77 C8 11 6B 62 C7 D6 79 AA EA AA 16 E1 B2 22 4D C4 01 AD 36 58 61 CA 6B 30 C6 E5 64 A0 B6 97 0F A6 A9 6F D6 71 DF C7 CF 3E 7F 37 93 66 8E C6 71 DE 92 4C C0 E1 22 0D FD 57 7A CB EE B6 CF EF 69 54 FD 66 44 05 31 D0 03 18 01 05 40 04 70 9C 51 46 AD 38 49 33 00 86 20 DD 42 88 04 22 5F A6 A1 DB F6 78 79 D4 79 95 76 1F 3F DF FD E7 98 86 16 B1 30 0B 65 D6 3C BD 2A 15 CE D8 AB E5 79 9D 47 7B DA 34 13 C7 34 73 5A 6B 0B 35 72 D9 5C 0D BB AE 53 AA E8 5F 86 B4 01 E9 25 8D 0D 50 8E 72 3C 39 3C B2 13 94 82 74 CE 2D C7 B3 41 8B ED 4C 9F F5 0B E2 85 6C 01 8C FE C7 B8 E9 87 8C D9 F1 90 28 A3 73 FE 05 6D DE 5F F1

正如你所看到的,在偏移量68( 78 9C )开始一个有效的zlibstream。 如果你在这里分割数据并膨胀zlib数据,它会返回一个如下的string:

U_HEAD01005300802P9QAN-40501201514560DEDE0080ID0200180104840080BL020357031204GW3HEMP906012015060120151021193517S0010018Fernweh-Ticket natS00200012S0030001AS00900051-0-0S01200010S0140002S2S0150006BerlinS0160011NeumünsterS0210038B-Hbf 8:16 ICE794/HH-Hbf 10:16 IC2224S0230013Krull AndreaS026000213S0270019***************0484S0280013Andrea#Krull S031001006.01.2015S032001006.01.2015S035000511160S0360003271

testingNSData

如果我使用从bcTester返回的字节手动构build字节数组,则一切按预期工作,并且zlib数据膨胀正确。 这是我如何testing:

 let testArray = [UInt8](arrayLiteral: 0x23, 0x55, 0x54, 0x30, 0x31, 0x30, 0x30, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x2C, 0x02, 0x14, 0x1C, 0x3D, 0xE9, 0x2D, 0xCD, 0x5E, 0xC4, 0xC0, 0x56, 0xBD, 0xAE, 0x61, 0x3E, 0x54, 0xAD, 0xA1, 0xB3, 0x26, 0x33, 0xD2, 0x02, 0x14, 0x40, 0x75, 0x03, 0xD0, 0xCF, 0x9C, 0xC1, 0xF5, 0x70, 0x58, 0xBD, 0x59, 0x50, 0xA7, 0xAF, 0xC5, 0xEB, 0x0A, 0xF4, 0x74, 0x00, 0x00, 0x00, 0x00, 0x30, 0x32, 0x37, 0x31, 0x78, 0x9C, 0x65, 0x50, 0xCB, 0x4E, 0xC3, 0x30, 0x10, 0xE4, 0x53, 0x2C, 0x71, 0x43, 0x4A, 0xD9, 0xF5, 0x2B, 0x36, 0xB7, 0x84, 0x04, 0x52, 0x01, 0x55, 0x51, 0x40, 0x1C, 0x51, 0x01, 0x23, 0x2A, 0x42, 0x0E, 0x21, 0x15, 0x3F, 0xC7, 0x8D, 0x1F, 0x63, 0x36, 0x11, 0x52, 0x2B, 0x7C, 0xF1, 0x78, 0x76, 0x76, 0x66, 0xBD, 0xF7, 0x8F, 0x4D, 0x5D, 0x54, 0xC4, 0x44, 0xCE, 0x10, 0x05, 0xD2, 0xEB, 0x78, 0x5B, 0xAC, 0x32, 0x7B, 0xB4, 0x77, 0xC8, 0x11, 0x6B, 0x62, 0xC7, 0xD6, 0x79, 0xAA, 0xEA, 0xAA, 0x16, 0xE1, 0xB2, 0x22, 0x4D, 0xC4, 0x01, 0xAD, 0x36, 0x58, 0x61, 0xCA, 0x6B, 0x30, 0xC6, 0xE5, 0x64, 0xA0, 0xB6, 0x97, 0x0F, 0xA6, 0xA9, 0x6F, 0xD6, 0x71, 0xDF, 0xC7, 0xCF, 0x3E, 0x7F, 0x37, 0x93, 0x66, 0x8E, 0xC6, 0x71, 0xDE, 0x92, 0x4C, 0xC0, 0xE1, 0x22, 0x0D, 0xFD, 0x57, 0x7A, 0xCB, 0xEE, 0xB6, 0xCF, 0xEF, 0x69, 0x54, 0xFD, 0x66, 0x44, 0x05, 0x31, 0xD0, 0x03, 0x18, 0x01, 0x05, 0x40, 0x04, 0x70, 0x9C, 0x51, 0x46, 0xAD, 0x38, 0x49, 0x33, 0x00, 0x86, 0x20, 0xDD, 0x42, 0x88, 0x04, 0x22, 0x5F, 0xA6, 0xA1, 0xDB, 0xF6, 0x78, 0x79, 0xD4, 0x79, 0x95, 0x76, 0x1F, 0x3F, 0xDF, 0xFD, 0xE7, 0x98, 0x86, 0x16, 0xB1, 0x30, 0x0B, 0x65, 0xD6, 0x3C, 0xBD, 0x2A, 0x15, 0xCE, 0xD8, 0xAB, 0xE5, 0x79, 0x9D, 0x47, 0x7B, 0xDA, 0x34, 0x13, 0xC7, 0x34, 0x73, 0x5A, 0x6B, 0x0B, 0x35, 0x72, 0xD9, 0x5C, 0x0D, 0xBB, 0xAE, 0x53, 0xAA, 0xE8, 0x5F, 0x86, 0xB4, 0x01, 0xE9, 0x25, 0x8D, 0x0D, 0x50, 0x8E, 0x72, 0x3C, 0x39, 0x3C, 0xB2, 0x13, 0x94, 0x82, 0x74, 0xCE, 0x2D, 0xC7, 0xB3, 0x41, 0x8B, 0xED, 0x4C, 0x9F, 0xF5, 0x0B, 0xE2, 0x85, 0x6C, 0x01, 0x8C, 0xFE, 0xC7, 0xB8, 0xE9, 0x87, 0x8C, 0xD9, 0xF1, 0x90, 0x28, 0xA3, 0x73, 0xFE, 0x05, 0x6D, 0xDE, 0x5F, 0xF1) let testData = NSData(bytes: testArray, length: testArray.count) 

我前段时间在Xamarin / C#中解决了这个问题,但Swift的想法也是一样的。 encodedDataReadCode方法取自ZXing lib。 希望能帮助到你。

它适用于阅读和解码“小”和“大”的门票代码,但iOS SDK默认的Aztec阅读器不够好,所以最后我们和Manateeworks的读者进行了交stream。 现在我可以看到,iOS 10 SDK没有更好的performance。

  public override void DidOutputMetadataObjects (AVCaptureMetadataOutput captureOutput, AVMetadataObject[] metadataObjects, AVCaptureConnection connection) { foreach (AVMetadataMachineReadableCodeObject metadata in metadataObjects) { var d1 = (metadata.ValueForKey ((NSString)"_internal")); var d2 = (d1.ValueForKey ((NSString)"basicDescriptor")); var data = (d2.ValueForKey ((NSString)"BarcodeRawData")); var str = data.ToString ().Trim ().Trim (new [] { '<', '>' }).Replace (" ", ""); var bitarray = new bool[str.Length * 4]; for (var i = 0; i < str.Length / 2; i++) { int value = Convert.ToInt32 (str.Substring (i * 2, 2), 16); bitarray [i * 8 + 0] = (value & 1) > 0; bitarray [i * 8 + 1] = (value & 2) > 0; bitarray [i * 8 + 2] = (value & 4) > 0; bitarray [i * 8 + 3] = (value & 8) > 0; bitarray [i * 8 + 4] = (value & 16) > 0; bitarray [i * 8 + 5] = (value & 32) > 0; bitarray [i * 8 + 6] = (value & 64) > 0; bitarray [i * 8 + 7] = (value & 128) > 0; } var pabData = encodedData (bitarray); parent.scanFinished (true, pabData); } } enum ZXAztecTable { ZXAztecTableUpper, ZXAztecTableBinary, ZXAztecTableDigit }; public byte[] encodedData (bool[] bitArray) { var result = new List<byte> (); int endIndex = bitArray.Length; ZXAztecTable latchTable = ZXAztecTable.ZXAztecTableUpper; // table most recently latched to ZXAztecTable shiftTable = ZXAztecTable.ZXAztecTableUpper; // table to use for the next read int index = 0; while (index < endIndex) { if (shiftTable == ZXAztecTable.ZXAztecTableBinary) { if (endIndex - index < 5) { break; } int length = ReadCode (bitArray, index, 5); index += 5; if (length == 0) { if (endIndex - index < 11) { break; } length = ReadCode (bitArray, index, 11) + 31; index += 11; } for (int charCount = 0; charCount < length; charCount++) { if (endIndex - index < 8) { index = endIndex; // Force outer loop to exit break; } byte code = (byte)ReadCode (bitArray, index, 8); result.Add (code); index += 8; } // Go back to whatever mode we had been in shiftTable = latchTable; } else { int size = shiftTable == ZXAztecTable.ZXAztecTableDigit ? 4 : 5; if (endIndex - index < size) { break; } ReadCode (bitArray, index, size); index += size; latchTable = shiftTable; shiftTable = ZXAztecTable.ZXAztecTableBinary; } } return result.ToArray (); } public int ReadCode (bool[] bitArray, int startIndex, int length) { int res = 0; for (int i = startIndex; i < startIndex + length; i++) { res <<= 1; if (bitArray [i]) { res |= 0x01; } } return res; }