Pdf417 Decode and Generate The Same Barcode Using Swift

PDF417 decode and generate the same barcode using Swift

There are a lot of things going on here. Gereon is correct that there are a lot of parameters. Choosing different parameters can lead to very different bar codes that decode identically. Your current barcode is "correct" (though a bit messy due to an Apple bug). It's just different.

I'll start with the short answer of how to make your data match the barcode you have. Then I'll walk through what you should probably actually do, and finally I'll get to the details of why.

First, here's the code you're looking for (but probably not the code you want, unless you have to match this barcode):

filter.setValue(codeData, forKey: "inputMessage")
filter.setValue(3, forKey: "inputCompactionMode") // This is good (and the big difference)
filter.setValue(5, forKey: "inputDataColumns") // This is fine, but probably unneeded
filter.setValue(0, forKey: "inputCorrectionLevel") // This is bad

PDF 417 defines several "compaction modes" to let it pack a truly impressive amount of information into a very small space while still offering excellent error detection and correction, and handling a lot of real-world scanning concerns. The default compaction mode only supports Latin text and basic punctuation. (It compacts even more if you only use uppercase Latin letters and space.) The first part of your string can be stored with text compaction, but the rest can't, so it has to switch to byte compaction.

Core Image actually does this switch shockingly badly by default (I opened FB9032718 to track). Rather than encoding in text and then switching to bytes, or just doing it all in bytes, it switches to bytes over and over again unnecessarily.

There's no way for you to configure multiple compaction methods, but you can just set it to byte, which is what value 3 is. And that's also how your source is doing it.

The second difference is the number of data columns, which drive how wide the output is. Your source is using 5, but Core Image is choosing 6 based on its default rules (which aren't fully documented).

Finally, your source has set the error correction level to 0, which is not recommended. For a message of this size, the minimum recommended error correction level is 3, which is what Core Image chooses by default.

If you just want a good barcode, and don't have to match this input, my recommendation would be to set inputCompactionMode to 3, and leave the rest as defaults. If you want a different aspect ratio, I'd use inputPreferredAspectRatio rather than modifying the number of data columns directly.


You may want to stop reading now. This was a very enjoyable puzzle to spend the morning on, so I'm going to dump a lot of details here.

If you want a deep dive into how this format works, I don't know anything currently available other than the ISO 15438 Spec, which will cost you around US$200. But there used to be some pages at GeoCities that explained a lot of this, and they're still available through the Wayback Machine.

There also aren't a lot of tools for decoding this stuff on the command line, but pdf417decode does a reasonable job. I'll use output from it to explain how I knew all the values.

The last tool you need is a way to turn jpeg output into black-and-white pbm files so that pdf417decode can read them. For that, I use the following (after installing netpbm):

cat /tmp/barcode.jpeg | jpegtopnm | ppmtopgm | pamthreshold | pamtopnm > new.pbm && ./pdf417decode -c -e new.pbm

With that, let's decode the first three rows of your existing barcode (with my commentary to the side). Everywhere you see "function output," that means this value is the output of some function that takes the other thing as the input:

0 7f54 0x02030000 (0)    // Left marker
0 6a38 0x00000007 (7) // Number of rows function output
0 218c 0x00000076 (118) // Total number of non-error correcting codewords
0 0211 0x00000385 (901) // Latch to Byte Compaction mode
0 68cf 0x00000059 (89) // Data
0 18ec 0x0000021c (540)
0 02e7 0x00000330 (816)
0 753c 0x00000004 (4) // Number of columns function output
0 7e8a 0x00030001 (1) // Right marker

1 7f54 0x02030000 (0) // Left marker
1 7520 0x00010002 (2) // Security Level function output
1 704a 0x00010334 (820) // Data
1 31f2 0x000101a7 (423)
1 507b 0x000100c9 (201)
1 5e5f 0x00010319 (793)
1 6cf3 0x00010176 (374)
1 7d47 0x00010007 (7) // Number of rows function output
1 7e8a 0x00030001 (1) // Right marker

2 7f54 0x02030000 (0) // Left marker
2 6a7e 0x00020004 (4) // Number of columns function output
2 0fb2 0x0002037a (890) // Data
2 6dfa 0x000200d9 (217)
2 5b3e 0x000200bc (188)
2 3bbc 0x00020180 (384)
2 5e0b 0x00020268 (616)
2 29e0 0x00020002 (2) // Security Level function output
2 7e8a 0x00030001 (1) // Right marker

The next 3 lines will continue this pattern of function outputs. Note that the same information is encoded on the left and right, but in a different order. The system has a lot of redundancy, and can detect that it's seeing a mirror image of the barcode.

We don't care about the number of rows for this purpose, but given a current row of n and a total number of rows of N, the function is:

30 * (n/3) + ((N-1)/3)

Where / always means "integer, truncating division." Given there are 24 rows, on row 0, this is 0 + (24-1)/3 = 7.

The security level function's output is 2. Given a security level of e, the function is:

30 * (n/3) + 3*e + (N-1) % 3
=> 0 + 3*e + (23%3) = 2
=> 3*e + 2 = 2
=> 3*e = 0
=> e = 0

Finally, the number of columns can just be counted off in the output. For completeness, given a number of columns c, the function is:

30 * (n/3) + (c - 1)
=> 0 + c - 1 = 4
=> c = 5

If you look at the Data lines, you'll notice that they don't match your input data at all. That's because they have a complex encoding that I won't detail here. But for Byte compaction, you can think of it as similar to Base64 encoding, but instead of 64, it's Base900. Where Base64 encodes 3 bytes of data into 4 characters, Base900 encodes 6 bytes of data into 5 codewords.

In the end, all these codewords get converted to symbols (actual lines and spaces). Which symbol is used depends on the line. Lines divisible by 3 use one symbol set, the lines after use a second, and the lines after that use a third. So the same codewords will look completely different on line 7 than on line 8.

Taken together, all these things make it very difficult to look at a barcode and decide how "different" it is from another barcode in terms of content. You just have to decode them and see what's going on.

Detect (and maybe decode) PDF417 barcodes using python

After various trials, I ended up using an approach of template matching by OpenCV.
You need to precisely choose your template image that will be the search reference of your algorithm. You need to feed it some grayscaled images.
Then, you need to choose the boxes that have a result higher than a certain threshold (for me 0.55). Then apply NMS (non max suppression) to filter out the noisy boxes.

But keep in mind that there are many edge cases to encounter. If someone is interested to see the complete solution, please let me know.

Sample Image

PDF417 barcode encoding variation

To encode a PDF417 barcode, the input text (e.g. "text") undergoes a number of operations, converting all the text into numbers, applying algorithms, etc. and out the other end comes rows and rows of blocks. These blocks are stacked on top of each other to create a 2D barcode.

It looks like the OnBarcode generator has the "Number of Rows" setting as defaulted to 30, forcing the barcode to be that number of rows. The extra space is filled with nothingness.
The Raco one defaults to Automatic, which doesn't need filling with nothingness.

Set the following settings to be the same on both, and they will both produse the same result:

  • Error Correction
  • Row Count
  • Column Count

Results from both providers when settings are the same

Raco

Raco Version

OnBarcode

OnBarcode version

Converting PDF417 codewords/binary array to ID Automation Barcode Font code

The ID Automation font is only going to "like" it's own proprietary encoding scheme, which appears to be using something similar to octal instead of the Barby binary or "tc" decimal. The three schemes all create different PDF417 barcodes with different number of rows and columns.

What you are running into is the stage gap between digitization and rendering. The digitization stage is responsible for turning data into the dots and spaces required for the specific symbology used (Code 128, PDF417, etc). The rendering stage is used to take the ones and zeros and turn those into ink or light up pixels.

You can't mix the two stages because the first stage creates the encoding in the format that the second stage needs.

I tried to convert the ID Automation encoding to binary from what I assumed was octal. It did not scan. The Barby binary encoding scanned like a champ.

// The MIT License (MIT)// Copyright (c) 2020, Notionovus, LLC.var array5bit_A = new Array ( 'f//AAAAAAAAAAAAAAAAAAAA', 'f//AAAAAAAAAAAAAAAAAAAB', 'f//AAAAAAAAAAAAAAEAAAD/', 'f//AAAAAAAAAAAAAAEAAAAA', 'f//AAAAAAAAAQAAAP8AAAAA', 'f//AAAAAAAAAQAAAP8AAAAB', 'f//AAAAAAAAAQAAAAAAAAD/', 'f//AAAAAAAAAQAAAAAAAAAA', 'f//AAABAAAA/wAAAAAAAAAA', 'f//AAABAAAA/wAAAAAAAAAB', 'f//AAABAAAA/wAAAAEAAAD/', 'f//AAABAAAA/wAAAAEAAAAA', 'f//AAABAAAAAAAAAP8AAAAA', 'f//AAABAAAAAAAAAP8AAAAB', 'f//AAABAAAAAAAAAAAAAAD/', 'f//AAABAAAAAAAAAAAAAAAA', 'QD/AAD/AAAAAAAAAAAAAAAA', 'QD/AAD/AAAAAAAAAAAAAAAB', 'QD/AAD/AAAAAAAAAAEAAAD/', 'QD/AAD/AAAAAAAAAAEAAAAA', 'QD/AAD/AAAAAQAAAP8AAAAA', 'QD/AAD/AAAAAQAAAP8AAAAB', 'QD/AAD/AAAAAQAAAAAAAAD/', 'QD/AAD/AAAAAQAAAAAAAAAA', 'QD/AAAAAAAA/wAAAAAAAAAA', 'QD/AAAAAAAA/wAAAAAAAAAB', 'SL/AADeAAAA/gAAAAIAAAD+', 'QD/AAAAAAAA/wAAAAEAAAAA', 'QD/AAAAAAAAAAAAAP8AAAAA', 'QD/AAAAAAAAAAAAAP8AAAAB', 'QD/AAAAAAAAAAAAAAAAAAD/', 'QD/AAAAAAAAAAAAAAAAAAAA');var array5bit_B = new Array ( 'US0CAuSD38g', 'UUYCA7QBErs', 'ajEDAm49ReY', 'UUoCA+juogg', 'bjEDAjQrOn0', 'bkoDA3iPVH4', 'ajUDAt82atY', 'UU4CA1nljTg', 'cjEDAghkmFU', 'ckoDA0TA9lY', 'izUEAhrxcbg', 'ck4DAxY8F10', 'bjUDAlvFFR8', 'bk4DAxdhexw', 'ajkDAr7LFAw', 'UVICAyQ+UJI', 'TTECAq7UnEM', 'TUoCA+Jw8kA', 'ZjUDAmZGozo', 'TU4CA7CME0s', 'ajUDAvnk9E4', 'ak4DA7VAmk0', 'ZjkDAtle3bI', 'TVICAxOyzrM', 'STUCAqHeHtM', 'SU4CA+16cNA', 'h6QEAZKdo54', 'SVICA62zYxM', 'RTkCAqx1lb4', 'RVICA/z3WM0', 'QT0CAkdoxRU', 'KFYBA46vJCA');var stringStart = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAACCAQAAADLaIVbAAAANUlEQVQIHQEqANX/A';var stringMid = 'AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAA';var stringEnd = 'AAAAASUVORK5CYII=" width="';
var fltHeight = 2.1;var fltWidth = 4.0;
function genBarcode(inputString,intWidth,intHeight) { var intRawmod = inputString.length % 5; if (intRawmod > 0) for (var i = 0; i < 5 - intRawmod; i++) inputString += "0"; var arraySeq = new Array (intChunks = inputString.length / 5); for (var i = 0; i < intChunks; i++) arraySeq[i] = parseInt(inputString.substr(i * 5, 5), 2); var resultString = ""; for (var i = 0; i < arraySeq.length; i++) resultString += stringStart + array5bit_A[arraySeq[i]] + stringMid + array5bit_B[arraySeq[i]] + stringEnd + intWidth + '" height="' + intHeight + '">'; return resultString;}
var buttonBarcode = document.getElementById("btnGenBar");buttonBarcode.onclick = function () { strResult = ""; fltWidth = document.getElementById("textWidth").value; fltHeight = document.getElementById("textHeight").value; for (var loop = 0; loop < pdf417Barby.length; loop++) { strText = pdf417Barby[loop]; strResult += '<div style="height:' + fltHeight + 'px">' + genBarcode(strText, fltWidth, fltHeight) + "</div>"; } document.getElementById("resultBarby").innerHTML = strResult; strResult = ""; for (var loop = 0; loop < pdf417IDA.length; loop++) { strText = ""; for (var innerLoop = 0; innerLoop < pdf417IDA[loop].length; innerLoop++) { chrOctal = pdf417IDA[loop].charAt(innerLoop); strBinary = parseInt(chrOctal).toString(2); while ( strBinary.length < 3 ) strBinary = '0' + strBinary; strText += strBinary; } strResult += '<div style="height:' + fltHeight + 'px">' + genBarcode(strText, fltWidth, fltHeight) + "</div>"; } document.getElementById("resultIDA").innerHTML = strResult;}
var pdf417Barby = ["11111111010101000111010110111100001100100000100110011101100100011000111000110110111001110010001000111010010001000010000100001101110011001101001000001100011101010111000000110000110001000101010011101110000010101000001000000111111101000101001", "11111111010101000111111010110111101110101110000011011110101110011100110101110000100001000011101011111011100000100011010100001110101111101011111010000111011100011001110100111100010000010101111101100100010011110101101100000111111101000101001", "11111111010101000110101000011111001100011111010001010000111100100010100000110010011101101010111110000010101000111100000111100101111010001101100001001111011111010111001000100011101100011101000111011000111010101100111110000111111101000101001", "11111111010101000101001000010000001000010011101110011110011101001100111101111010001001100011000010010010001100110010000101100111000110001100001100010001011000011011001100100010000110001101101110011000100011101001001110000111111101000101001", "11111111010101000110101111001100001011111010000111011000001001110010100010011111011001110011011101000010111110100001110100001110101111101110101110000011011110100000101000111010111001100001010111111011100011110101111011110111111101000101001", "11111111010101000111010111110010001000011110010001010001111000010100100001111001000101000011110010001010111111011100010101001100111110001111110010110001010111111011110110110110001011110001011001101111000011010111111001000111111101000101001", "11111111010101000111101000101111001011011101100000011001100001100110101101110011000001011100111000100011101111000110010110100001100011101000110110001000010101100011000000111001100000101101110011011101111010100111100111100111111101000101001", "11111111010101000111110100100001101011100010011111011101001100000010110011000011110101111010010010000011110101000100000101001111110111001100110001111010010100011110000100101000111100001001010001111000010011111010010001100111111101000101001", "11111111010101000111111010011101101111001011110100011011010000011110100001101000111001111101000011101011001111010011000100011101110111101101111100010001010001110110001110100011101100011101000111011000111011111010011110110111111101000101001", "11111111010101000101000010010000001000010011101110011110011101001100100000100010001001100111001110011010000100111011100100001001110111001000010011101110011110011101001100100000100010001001110001100001011010100011110111100111111101000101001", "11111111010101000101001111001000001111000100000101011110001000001010111100010000010101111000100000101010001111001011110111000110011101001111000100000101011110001000001010111100010000010101111000100000101011010011110110000111111101000101001", "11111111010101000101000110011111001100111101001100010000001101011100110111110001001001101111100010010011011111000100100110111110001001001101111100010010011011111000100100100111100001001001000111011101111011110100011110010111111101000101001", "11111111010101000110100000100001101001110011100100010011100111001000100111001110010001100001100010001011000011000100010100001100110010001000010100010000011110011100101100100001000110001101101001100001110011101000001001110111111101000101001", "11111111010101000111010001100010001111011100011101011011101111100100110001101111010001111000010010010011010111110011100101111011011111001111000100000101011110001000001010111100010000010101111000100000101011101000110010000111111101000101001", "11111111010101000100101011110000001000111011000111010001110110001110110011110100110001000000110101110010011110000100100100011101110111101001111000010010010001110111011110100111100001001001000111011101111010010100011110000111111101000101001", "11111111010101000100101100001100001100111001110011010000100111011100100001001110111001100001100011011011001110011100110100001001110111001000010011101110010000100111011100110000110001101101001110011100100011001011001110000111111101000101001", "11111111010101000111110100001001101000011101011111011100001000110100111000010001101001011110110111110011110001000001010100011110010111101110001100111010011110001000001010111110110010001001011111001000111011111010000101100111111101000101001", "11111111010101000111001011111010001000000110100111010001110110001110100011101100011101000111011000111010001110110001110100011101100011101000111011000111010001110110001110100011101100011101000111011000111011001011111100100111111101000101001", "11111111010101000110110110110000001111001110100110010000010001000100111000110000101101000010011101110010000110011100110111011100110011001111001100010111011100011011011100111001000100011101011001110011000011101101000001100111111101000101001", "11111111010101000111010000011000101011110110111110011110001000001010100110001111101001100010111000010011110101110111000101111011011111001111000100000101011110001000001010111100010000010101111000100000101011110100000110110111111101000101001", "11111111010101000110010010001111101000111011000111010001110110001110100011101100011101000111011000111010001110110001110100011101100011101100011111001010011011111000100100100001111001000101001111000010010011111100100110100111111101000101001", "11111111010101000100100111001110001000110111100111011100011000010110100001001110111001000010011101110011110011101001100100000100010001001000110001100001010001101111001110100111001110010001001110011100100010110111000011000111111101000101001", "11111111010101000111001011001000001011110110111110011110001000001010111110110010001001000000101111010010011110010111100111100010000010101111000100000101011110001000001010111110110010001001011111001001110011110010110110000111111101000101001", "11111111010101000111011011111100101000000110100111010001110110001110100011101100011101000111011000111010001110110001110100011101100011101000111011000111010001110110001110100011101100011101000111011000111010110010011100000111111101000101001", "11111111010101000111011001100111001000010011101110010000100100000010101110111000010001101100001000010011100111010010000110011100111001101000010011101110010000100111011100100001001110111001000010011101110011001000100110000111111101000101001", "11111111010101000110010111100001101111000100000101011110001000001010111100010000010101101110001111101010111110100001110111000010001101001110001100111010011110001000001010111100010000010101111000100000101011001011110001100111111101000101001", "11111111010101000101100111001111101111100010011101010000011100101100100001111001000101000000110101110010011110000100100100011101110111101001111000010010010001110111011110100111100001001001000111011101111011101100111111010111111101000101001", "11111111010101000111011000101100001000110001100001010001101111001110100011000110000101000110111100111010001100011000010100011011110011101000110001100001010001101111001110110011100111001101000010010000001010010001110001110111111101000101001", "11111111010101000111101101100010001100010111000010011000100011110110110011000011110101101011100000100011110100100100000111101000100000101011110110111110011110001000001010111100010000010101111000100000101011111011011001100111111101000101001", "11111111010101000101100001000111001000111011000111010001110110001110100011101100011101000111011000111010001110110001110100011101100011101000111011000111010001110110001110100011101100011101000111011000111010110000010111000111111101000101001", "11111111010101000100100001100110001000010011101110010000100111011100100001001110111001000010011101110010000100111011100100001001110111001000010010000001010111011100001000110001000001001101110011101001000010110001100000100111111101000101001", "11111111010101000110110111100001001110000100011010011100001000110100111000010001101001011110110111110011111011001000100100111100010111101001111001011110011110001000001010100110001111101001100010111000010011011011110001000111111101000101001", "11111111010101000100010111011111101101110100011100010011110100100000110101011111000001111110010111011011010011111100010100011101000110001000010010111100011111001011101000100111101001000001010111000111111011000101111110100111111101000101001", "11111111010101000100100000100000101111001110100110010000110001100100100001100011001001000011000110010010000110001100100100001100011001001110111100110001011100010001001110100001000010100001011000011101100011001000001000110111111101000101001", "11111111010101000111110110010001001111010010001000011100010111000110101000111100010001001111101000111010111101001111000111000101100001001111000001010100011100010001100010111100010001001001110001101110010011111101100100110111111101000101001", "11111111010101000111111000100110101101001100111111010100000100011110101111011101110001010000100001111010001111000101000101001110111111001000111010110000011100010011111010110111110001010001011000011011110011111000100111010111111101000101001", "11111111010101000111000101100111101000011011000001011011011001100000101000001010000001110100111011111010011011110111000100001100111001101000101110111000011110011110100010100011100111000101110101101111000011000101101110000111111101000101001", "11111111010101000110110011110001001011111101001100011111000011010010111001110111100101101000011110110010110111111011000110000101110010001100110011110001010010011110000100111101000110110001111010111000111011011001111001000111111101000101001", "11111111010101000101110110111000001100111000101110011010011011111100111111010110001001000010011011111011101001011111100111001001011111101100110010111100011111010000111010101000000101111001100111110001001010111011000111000111111101000101001", "11111111010101000111000100100111001000101110001110010111011110110000101000010001000001100110011100111011101110010100000111000110110111001100001101101100010011011100001100100011000110010001111011110110110011100010010111000111111101000101001"];var pdf417IDA = ["777777770707070007737352123166300076631353225166610734303675155600007111427613176322077740145751563460750112671207373307462673170014623076600377166364430764227111623264707206231512236203072202551566304630740621733221460107662617154430221076311157202731250704117660656332207440533422766013077771707415447400777777707000707007", "777777770707070007574363351354550072313512245101460770433551033675207467512777271774076462315526366430725123004422375207723531663457220072657406674037100764401175263464107243110276220673074026137726740230702267312642323507651004714437752072652177441755110762661351203600107735245473154564077161273111541300777777707000707007", "777777770707070007373074471114744075005377074633220750053770746332207500537707463322077205157054631200750013770306332607440537217563333074000377120722270740003771207222707004473356432623074000377120722270740003771207222707620037712272007076022355122722070760223551227220707602235512272207077372124135523000777777707000707007", "777777770707070007735210263753230070266713744326230720445317663062107226653356410601070044733564326230722445335663040307026671374432623072044531766306210762220671214131407005573246432632070045732575237330700557324643263207000573217523337074451376424326720744017761712733307445177642432272077170122611550300777777707000707007", "777777770707070007372030142315331074440377524326630744403775243266307664037752632443072026715566326470760223551227220707620215512270027074022177322502050762221751007022507400017532270205074000377120722270700447335643262307400037712072227070044733564326230700446235652373207400037712072227073702101621311510777777707000707007", "777777770707070007772070551355651076000157122700250740003771207222707000077356472667074404773164326630744403771207266707440037716436663074400733160326670740442275612777207445137642432672074441376535237730744403775243266307445137642432672075441773434673220750017330346376607104177303067322071340563333154330777777707000707007", "777777770707070007631611404621131077463355436237420774633554362374207302771547623746073027711076233060730277154762374607306771147623702073265711456015020752233750124332407700315503261106075203377210433040730277514766334607702335507627702073027751476633460730673554366330207742375143267302076334314004611310777777707000707007", "777777770707070007336314652757330077473254426337520765337055626774607302765157772346072077355437723120760663111623224307642264516327312072215530044122160766411565152357107665137642632452076463354537237530764733544263265207646335453723753076473354426326520764633545372375307647335442632652077325124741155740777777707000707007", "777777770707070007552072225115741073207717674017440730277110762330607302771547623746073027711076233060730277154762374607302771107623306077463355436237420774637514366730207702371103663746073063751032673020730237514722730207702335503263306073023351076233460774277114322374207306331547267702077502522271137450777777707000707007", "777777770707070007277204655201577075441277535327620757633757141076207664015750432461076651157405324610767412665373244207656324553722642076642155526304630754620777361065107767317550530760077440057536324730744412664342377307555166343576322074011732124726660703637700035733107502313723601740076770460134075510777777707000707007", "777777770707070007314201277155021072011772126623420722317500374027107301055313762162071323651317622500732235731246104107203377022672252073133562334511610712317733275225007111174323750071071112472337502610712307722355315007222275223561153073220461216511510712276334651335507237435772313773077126032375134450777777707000707007"]
<head><title>PDF417 Example</title><meta http-equiv="Content-Type" content="text/html;charset=utf-8" ></head><body> <input type="button" id="btnGenBar" value="Generate Barcode" tabindex=4/>  ↕ <input type="text" id="textHeight" size="2" maxlength="3" tabindex=2 value="2.1"/>  ↔ <input type="text" id="textWidth" size="2" maxlength="3" tabindex=3 value="4.0"/> <p>Barby:</p> <div id="resultBarby"></div> <p style="margin-top: 100px">ID Automation:</p> <div id="resultIDA"></div></body></html>


Related Topics



Leave a reply



Submit