4D Source Code for IDAutomation.com Code 128 Barcode Fonts
Copyright © IDAutomation.com, Inc. 2005, All rights reserved.
NOTE: The following code is an example that was provided by:
IWAC AB http://www:iwac.se e-mail: [email protected]
IDAutomation.com, Inc. does not guarantee, support or troubleshoot this source code or documentation.
Encoding Bar Code 128 in 4D
Scope
This article and links are primarily intended for 4:th Dimension programmers but may contain useful information to others as well.
It is possible to use 4D in such a way that 4D in it self creates the barcodes graphically with code alone; see technote "02-20 Bar Codes". This technote makes a great job graphically, but lacks some functionality when it comes to the embedded control codes.
The data supplied here deals with the situation when a system installed 128-barcode font has been installed into the system and 4D is called upon to print readable barcodes.
I used a barcode font from http://www.idautomation.com/ since they could offer both PC and Macintosh compability in one package whereas other vendors could not.
Author
My name is Kaj Björkman and I run a small company that specialize in 4D development with both Swedish national companies as well as international customers. More info can be found at the IWAC AB's homepage.
Any questions can be sent to this e-mail address: 4D BC128 questions .
Straight to the solution (i.e.: I do NOT read manuals)
The 4D Code. But beware: you need to modify 12 special characters depending on the font you are using! On Macintosh the operating system 9.x or X also makes a difference! Even if the font is from the same manufacturer.
What's going on?
I'm guessing here: you bought a 128 barcode font and installed it. Then you tried to print something. It failed in the reader. Even worse: you had experience of 9/3 barcode fonts: a * in the beginning and a * at the end solved the problem. Sorry, BC-128 does not work that way.
Or maybe you have more experience? Starting with solving the 'B'-startcode mystery, calculating the modula 103 checksum, but finding the barcode to be too long? Lots of 'number crunching' maybe? Barcodes getting too wide?
BC-128 is a different animal. It is rather sophisticated, at least in comparison to the 9/3 standard.
It uses special characters that turns modes 'on' and 'off'. If one mode is 'on' it can be switched to another mode. But not by using the same start character of that mode you want to go to! Confusing? At first, yes!
And different modes can overlap each other! Want to know 'What's going on' ? Read on!
My way of explaining!
If I try to explain everything perfectly technically correct it will be boring, hard to understand and difficult to write! So I will lie to you! Not to confuse you! Rather, the lying lies in not breaking the whole story in one go (i.e. sentence, paragraph). Read this text from here in sequence; just like listening to a teacher or viewing a film.
Eventually all will be revealed.
Start!
Fonts have no intelligence, so you have to provide it!
Let's begin with the simple notion that a BC-128 is built with a:
-Start character
-Data to be sent / received
-End character
Modes, the start character
There are basically 3 modes: 'A', 'B' and 'C'. If the barcode starts with a special character indicating 'A'. 'B' or 'C' the reader will switch into this mode and read the following letters in a manner that:
A: Send special characters to the receiving computer such as 'returns' and 'tabs' (and more!)
B: Send 'anything' seeable printable, preferably letters.
C: Send numbers in a compact form. A 12 digit number will only take 6 positions: compact code!
Easy way out
If no special characters (as 'A' above) is needed in the barcode 'B' is the safest bet! You can send the most common characters. Mix numbers with letters. Choose uppercase or lowercase. Send some graphical characters like right/left brackets, dots commas, etc.
The road split
Apparently sending barcode-128 in mode 'A' is for special occations/applications. Sending a 'tab' key to the receiving system requires the receiving system to be expecting this! You could send the same key to the receiving barcode reader as if the operator had pressed the 'Esc' key! The 'A' mode is very nice for custom built applications! My contribution to the 4D community excludes this mode for this reason. Sure, the 'A' mode can send uppercase letters and numbers too, but that is just a bonus when using it.
-Start character ('B' or 'C')
-Data to be sent / received
(-Possible code switcher
-Data to be sent / received)
-End character
We are left with 'B' and 'C'
These 2 modes are the most interesting here. I mentioned 'B' was preferably used for letters earlier. Why? Because numbers are usually better off with 'C' code.
Usually. Not always! Here it is:
If the barcode starts with a 'C' starting code the reader will read each sign as 2 numbers each. They are paired. So if you want to send 2739557809, using the 'easy way out' using code 'B' that would be printing 10 signs in the barcode. It will work, but using the 'C' start code groups your numbers into pairs. Each pair is then printed as ONE character. This cuts the barcode into half the length.
What if the number is not an even number of numbers? One solution is to add a starting zero. Another to use 'B' coding. Or switch between 'B' and 'C' !
Switching
The start-code turns the code into reading in a certain way. But the code can be switched within a single barcode! The switching character depends on in what mode has been started and where to go. The 3 start codes at the very beginning cannot be used again. My 4D code will handle:
B-start
C-start
B to C switching
C to B switching
If the data to be sent is:
81346524036SE
The start must, obviously, be 'C-start', grouping the numbers into pairs: 81,34,65,24,03. The poor '6' is left out in the cold without a proper partner. Grouping him with a letter is not possible unless switching to 'B' is made. This is where a 'B'-switch (not B-start!) is needed: go to letters! The '6' will then be treated together with the rest as letters ('6SE').
-Start character 'C'
-Data to be sent / received: 81,34,65,24,03
-Code switcher 'C' to 'B'
-Data to be sent / received: 6SE
-End character
Bad? It get's worse..
Now, having gone through all that, can it possibly be more complicated? You bet!
The BC-128 requires a checksum! What is a checksum? Well, it is a calculation of all things that have passed before it arrives. It could be a single digit like 0-9 (very common, a crude swedish commented version in 4DCode here). But it uses another technique. Namely the Modula 103. 4D supports it directly. (What is that? Read on.)
But how? Based on what?
BC-128 checksum
Every time a character is sent off to be printed (code starters and switchers included) a 'weight' of that 'letter'/'number pair' is used in a formula to add up a 'total weight'.
So it is now:
-Start character 'C'
-Data to be sent / received: 81,34,65,24,03
-Code switcher 'C' to 'B'
-Data to be sent / received: 6SE
-Checksum character
-End character
You might think: "aha, just add the numbers together". Not so easy I'm afraid.. You have to make some more arithmetic's. Every time you print a character into the barcode you must keep track of the position too. The start code is #1. Easy. But the 1:st character to print ALSO has a multiplier of 1! Confusing, but so it is. So the second character (3:rd to be printed) has the weight position of 2. Then it gets logical.
| start code | 1:st char | 2:nd char | 3:rd char |
Position multiplier | x 1 | x 1 | x 2 | x 3 |
Weights vs ASCII codes
The barcode characters all have weighting numbers from 0-105. These are NOT the ASCII numbers. Actually the weighing is simple between 0-94, the printable characters. Just add 32 and you have the ASCII number. 95-105 are supposed to have an offset of 100. But I have found out they do not. This is the reason these codes are set up as variables in my code; it is up to the programmer to find out where they are. Tough, but true.
Also, the weight of zero has it's own life. As you will find out shortly.
So how do I get the total?
By multiplying the character weight, as described above, with the position. So if the barcode is 3 simple letters like "JACK" you will have this arithmetic:
What -> | Start B | J | A | C | K |
Position multiplier -> | x 1 | Pos x 1 | Pos x 2 | Pos x 3 | Pos x 4 |
Weight -> | 104 | 42 | 33 | 35 | 43 |
Column total -> | 104 | 42 | 66 | 105 | 172 |
Accumulated total -> | 104 | 146 | 212 | 317 | 489 |
Add the Column totals: 104+42+66+105+172 = 489.
Note that the stop character lacks weight and has no part in the weight calculation.
Transform the total into a checksum using Modula 103
Calculate the total divided by 103 and you get how many times 103 will fit into the total: 489/103 = 4.7475xxxxxx.
Use only the 4, skip the fractional's and multiply it with 103: 4 x 103 = 412. The modula 103 is the difference between these:
489 - 412 = 77. That's it. The modula 103 for the total 489 is 77. A quick look into the table of the 128 code fonts will reveal that the 'letter' 77 is a Carriage return (in mode A), the letter 'm' in code B and the number pair '77' in code C. Since this is the modula checksum we are going to print; any possible character this position may hold is irrelevant (but equal to code 'C' if >0). The barcode scanner will only look at the weighing! In effect: 77. Anything else, and the barcode will not be readable. And that is by design.
Zero will be the 'joker'-card, but that will be revealed further down.
If you think this was tricky, wait until you start switching between code sets!
Worst is: you might really want to do this!
Consider the following data to be sent: "81346524036SE". Easiest way out is, off course, to use only one code all the way through! You could then use 'A' or 'B' but not 'C'. The reason being that 'C' can only send numbers. In this description I choose to leave the 'A' code out due to it's real purpose, namely to send special characters.
This approach will work, but has one major caveat: the printed code will be unnecessary long/wide! 'C'-code will make the printout very dense, actually cutting it in half the length. As mentioned earlier, this is because each single character sent as a barcode pattern will contain 2 numbers. But this will require some analyzing on the data to be sent. Only number pairs will work, so beginning with a 'C' code and then grouping numbers will work only until the number '6' is to be printed. Firstly because the '6' cannot make a numbers pair. The letter 'S' cannot be sent in code 'C' either.
We have got: '81', '34', '65', '24' and '03' as number pairs. Then we must make a code-switch from 'C'-code to 'B' code. Please note that the code switch character is different from the start character! Actually the start-character of any of the 3 available, can only be sent in the beginning of the barcode. Well.. the character in itself can be sent later, but then only as a checksum character!
Wow! This is getting real messy! Let's go trough this as an example, step by step and see if we can 'de tangle' the mysteries. Here's a table with the number pairs only:
What (grouped in numbers pairs)-> | Start C | '81' | '34' | '65' | '24' | '03' |
Position multiplier -> | x 1 | Pos x 1 | Pos x 2 | Pos x 3 | Pos x 4 | Pos x 5 |
Weight -> | 105 | 81 | 34 | 65 | 24 | 03 |
Column total -> | 105 | 81 | 68 | 195 | 96 | 15 |
Accumulated total -> | 105 | 186 | 254 | 449 | 545 | 560 |
Do you see the beauty in this? The numbers pairs = to a weight! They are equal in code 'C'.
OK, if you are with me, we need to insert a 'switch to 'B' mode' after the numbers pair '03'. When that is done we can continue sending characters one by one (instead of pairs).
This, however creates a new position! Pos x 6.
What -> | Start C | '81' | '34' | '65' | '24' | '03' | Shift B | '6' | 'S' | 'E' |
Position multiplier -> | x 1 | Pos x 1 | Pos x 2 | Pos x 3 | Pos x 4 | Pos x 5 | Pos x 6 | Pos x 7 | Pos x 8 | Pos x 9 |
Weight -> | 105 | 81 | 34 | 65 | 24 | 03 | 100 | 22 | 51 | 37 |
Column total -> | 105 | 81 | 68 | 195 | 96 | 15 | 600 | 154 | 408 | 333 |
Accumulated total -> | 105 | 186 | 254 | 449 | 545 | 560 | 1160 | 1314 | 1722 | 2055 |
Calculating the modula: 2055 / 103 = 19.95xxxx. 19 x 103 = 1957. 2055-1957 = 98.
Is this all?
Almost. When two zeroes are grouped as a number pair there is yet one more special character that must be handpicked from the barcode font. This is also reflected in the code, see below. The modulus 103 check-character can also turn out to be zero, in this case the 'twin zeroes' value must be sent. If you check the weight zero you will find out that this position prints nothing. It is a space after all.
The sister kind of flaw exists in the whole arithmetic of the effort in eliminating wrongful reads in the 128 standard. You can only go so far. It sure is more sophisticated than many other barcodes (like the 3/9) and in addition to the benefit of being able to send other data than just numbers, it is doing a good job.
The beauty is that..
I have done most of the work for you (if you are a 4D programmer)! The code analyzes the input string and tries to optimize the incoming data into a code switching scheme that makes sense. It is not a perfect design due to the fact that it does not calculate different options on optimization with additional code-switchers but it does a decent job.
I would also like to add a disclaimer; use this code at your own risk. I have tried to find all problems and bugs, but there could still be (I would be surprised if there was not!) bugs in there, probably dependant on the input strings.
You are invited to contribute by finding bugs and solutions to any problems (my email is within the code). Just note that I too have found that this code could be broken up into sub-codes as a strategy to compact/optimize the code. I have avoided this on purpose: It is by far easier to copy one code than several and then linking them together!
The variables could be transformed into data lists or even table entries; also avoided for the same reason.
The use of Substring rather than the exact positioning symbol is also on purpose. 4D uses 2 separate ways on defining this command object depending on platform. PC uses the double brackets and the Macintosh the 'bigger or equal to' and the 'less than or equal to' sign in the same place. Another example, although not used in this particular code is the sign for 'super global' variables. The PC uses the <>myVariable whereas the Macintosh uses a single diamond preceding the variable name.
Reading and de-engineering the code
At the moment I have not done more than a few remarks in the code. This description should go a long way though, but here is a very short one:
Basically I'm building an array with letters in the beginning. This array is first created in a very 'crude' format; it looks for a chance to switch code to 'C' with the simple assumption that 3 numerical's in a row will merit this action.
This array is then scrutinized if it can hold water on actual data, it must be numerical. After this the code checks that all the 'C' codes are in number pairs.
Then it is moving on to find out how many code switchers there will be. They will occupy positions, so this is important in order to create a well defined building loop later on.
Then the heart of the code comes into play. Here every position, including additional code starters are accounted for. The true start-code is being found and sent only once. The current code in force is being tracked.
Code 'B' and 'C' are being treated differently. The printing position is kept separately since 'C' produces only 50% of sent data. Double zeroes are treated, checksums are calculated in all modes. If ASCII-characters can be arithmetically deduced to Barcode 128 values (32) this is done, else the values are drawn from constants.
At the end the modula 103 is being calculated (4D supports this calculation in one command directly). Again, if the value of the checksum differs from zero and is no higher than 94 the correct character to print is calculated with the offset number 32. Otherwise the correct character to send is found by local variables.
It all ends with...
The stop character! This character has no value and is different from all the other patterns in the 128 code: it is both wider and has one element more than all the others.
Implementation suggestions
It is almost impossible to find the special characters unless setting up a small development test pad and a Code 128-pattern table (graphically seeing the bars for each printable character).
Create a form and a code that allows you to view any barcode character together with its ASCII code. The actual printed character in any other standard font may be interesting to see, but really has very little bearing on the special characters like 'twin zeroes' and values between 95 to 105. And the stop sign.
Then check that the installed barcode font shows up correct from 1 (zero, space will be blank!) to 94, always with an offset of 32. The ASCII 49 should have the weight of 17 (and print a '1') for example.
Passing value 94 (ASCII 126) will probably shoot off into ASCII >100. This area must be mapped up by you on looking at the actual bars to match the patterns of the symbol. You may find more than one match for any given character. On a Macintosh with OS 8.6 and IDAutomation I found the value character 96 at ASCII codes 115, 102 and 128. What you pick is your choice, as long as the printout is correct.
Validating your results can be done without a barcode reader, but I vote against it. Check them, preferably in the end system with the reader that will be used. This becomes very important when it comes to very small barcodes or very wide ones. In my experience a rather wide print could not be read by my simple and cheap reader unless I put the barcode in a photocopier and reduced it. The end customer, on the other hand could read it without any problems and demanded the with/size due to the reading distance (postal packages).
In some applications distance before the start and after the stop may have to be inserted so that the reader does not get confused. I think you will both see the dangers, if there are any, and avoid them without adding 'white-space' or, as the Germans say: 'ruhezone'.
A final note for those developing applications that must encode parcel tracking numbers. In Sweden, at least, these requires a different checksum calculation than the BC128 code in itself. Here is the code to do that.
Good luck!
No comments:
Post a Comment