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

标签 ios objective-c swift zlib aztec-barcode

一段时间以来,我一直在为 Deutsche Bahn(德国铁路)使用的一种特殊条形码开发 iOS 阅读器应用程序。它是连接 DSA 签名字符串和 zlib 压缩负载的 Aztec 条形码的变体。

当我得知 AVMetadataMachineReadableCodeObject 没有用于读取条形码中包含的原始字节的公共(public)方法并且字符串方法总是使 zlib 压缩数据出现乱码时,我陷入了困境。

幸运的是,this answer 让我朝着正确的方向前进。可以使用 KVO 访问(私有(private))字节,因为我目前不希望在 App Store 上分发该应用程序,所以这是完美的。

尽管我几乎不存在 SwiftObjective-C 知识,但我设法让它工作,正如您在示例代码中看到的那样。但是存储在 NSData 中的条形码中的字节与预期结果不匹配!我怀疑我使用的 zlib 库( DeflateSwift )不起作用,所以我构建了一个测试用例,它运行良好。

我的问题是:我做错了什么?我是否需要进一步处理原始字节以获得预期结果(见下文)? 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
这是我用于测试的德国铁路车票的示例条形码。
sample Deutsche Bahn Aztec barcode

这就是我需要的

当使用适当的条形码阅读器(我使用 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) 开始一个有效的 zlib 流。如果您在此处拆分数据并膨胀 zlib 数据,它会返回如下字符串:
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
测试 NSData

如果我使用从 bcTester 返回的字节手动构建字节数组,一切都会按预期进行,并且 zlib 数据会正确膨胀。这是我的测试方法:
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 的阅读器。我现在可以看到 iOS 10 SDK 并没有变得更好。

    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;
    }

关于ios - 通过 AVMetadataMachineReadableCodeObject 从 Aztec 条码读取原始字节会产生意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37225276/

相关文章:

ios - 可以将单例设置回零吗?

ios - 无法设置 cocoapods。 Cocoapods设置问题

objective-c - CIAreaHistogram 给了我除了最后一个元素之外的所有 0?

ios - 没有更多上下文,表达类型不明确

swift - 对数组索引的操作

ios - CoreBluetooth 在预定时间在后台运行

ios - 如何在 Swift 中旋转 UIButton 和 UILabel 的文本?

ios obj.var = exp VS [obj var :exp]

iphone - 如何识别AudioStreamer的isBuffering状态或As_Waiting_for_queue_to_start状态?

arrays - 如何从数组中随机过滤和选择 CGPoint