December 18, 201510 yr Hi all I am writing this post because I would like to share a custom function I created for generating EAN-13 barcodes. As almost every other (paid) solution involves plugins, scripts, fonts or other garbage, I decided to create something much cleaner. Everything the file needs is two custom functions, one that calls the other. The first, "GenerateEAN13_Raw" creates the binary string starting from the actual number, and the second, "GenerateEAN13_BMP_380px" takes the same number and generates a BMP file (that can be stored in a container) with the actual rendered barcode (sharp and clear). It took me many hours to create this, so I hope this will be useful for somebody. I just wanted to post everything to FMFunctions but the site is currently down. I know someone should not trust unknown download links, but I'm not attaching viruses here. The proof of concept is hosted on my site here: Download Feel free to use the code as you wish. No, this is not for UPC or other barcodes, just EAN-13. You can take the basic idea and then make yours. Some of the code was inspired by another solution, but in the end I had to recreate even the BMP header from scratch.
December 18, 201510 yr 4 hours ago, Mc128k said: No, this is not for UPC or other barcodes, just EAN-13. To make an EAN-13 symbol that is the exact equivalent of a UPC, append a 0 to the front of the UPC before passing it into a barcode generation routine.
December 18, 201510 yr Author 2 minutes ago, jbante said: To make an EAN-13 symbol that is the exact equivalent of a UPC, append a 0 to the front of the UPC before passing it into a barcode generation routine. I meant it's not for the generation of UPC codes. I may consider making it in the future, although the code is now public and anybody could follow the basic idea and make the rest.
December 18, 201510 yr 43 minutes ago, Mc128k said: I meant it's not for the generation of UPC codes. I may consider making it in the future, although the code is now public and anybody could follow the basic idea and make the rest. I'm not sure quite what you mean by this. Making a UPC version of your solution is literally as simple as using "0" & UPC as the input to your GenerateEAN13_RAW function. It doesn't just make an EAN-13 that decodes similarly — the result is a valid UPC barcode. EAN was designed as a superset of UPC specifically to work this way for intercompatibility with the older system. As for generating the sequence of digits for a UPC, yes, your custom function doesn't do that. It doesn't do that for EAN codes either. Both use codes issued by a central authority, except for certain ranges of codes for retailers to use for their own products.
December 18, 201510 yr Author I never studied how UPC works, this is new stuff for me. So UPC works just like EAN (with checksum and stuff) but with a zero at the beginning in the input number? Do you think using UPC has advantages over EAN? Of course the function can't generate codes, it's meant for existing articles or scalar numbers for a ticketing application I created.
December 18, 201510 yr 7 hours ago, Mc128k said: proof of concept is hosted on my site here: Download Feel free to use the code as you wish. No, this is not for UPC or other barcodes, just EAN-13. You can take the basic idea and then make yours. Some of the code was inspired by another solution, but in the end I had to recreate even the BMP header from scratch. Hi Mc128k and welcome to the FM Forums, Please attach your file here, or at Brian Dunning’s site LINK. We prefer this instead of being posted where we can not control the availability of it in the future. If this is a problem, let me know by private message. Lee
December 18, 201510 yr 4 hours ago, Mc128k said: So UPC works just like EAN (with checksum and stuff) but with a zero at the beginning in the input number? Do you think using UPC has advantages over EAN? Of course the function can't generate codes, it's meant for existing articles or scalar numbers for a ticketing application I created. EAN is based very heavily on UPC. I do not think using UPC has advantages over EAN. EAN can handle input that UPC can't, but it's a little facetious to talk about them like they're different things when it comes time to implement them — a little like asking if driving a car is better than driving a Ford. The only good reason I can think of for using the UPC/EAN symbology(ies) is to identify products that have UPC/EAN/GTIN IDs assigned by GS1 and unique among all other vendors, or to be compatible with a system that includes mostly such products. I can't make a recommendation tailored for you without knowing more about the constraints of your application. But Code 128 is the typical default I suggest when there aren't any particular constraints in mind. I made it the default in Barcode Creator for a reason.
December 19, 201510 yr Author Well, excellent work with Barcode creator, I see that Code 128 looks pretty much a hassle to implement. My solution doesn't have specific needs, so I chose the most common just to be compatible with all readers. And just to inform everybody, I am going to post online the functions, when the appropriate website will work, now it looks offline.
June 14, 20178 yr Author As Lee asked, I'm posting here the functions. Also I updated the docs. /** * ===================================== * BarcodeEAN13_BMP_380px ( number ) * * PURPOSE: * Generate a binary BMP file containing an EAN-13 barcode * * NOTE: * Embed the contents in a container to see it * * RETURNS: * Binary BMP. * * PARAMETERS: * number: The number to encode in EAN-13 * * DEPENDENCIES: * BarcodeEAN13_Raw * * RELEASE: * 2017-06-11 * * VERSION: 1.0 * * AUTHOR: © 2017 Mc128k * ===================================== */ Let ( [ ~pad="000000000000"; ~toEncode=SerialIncrement ( ~pad ; number); ~binary=BarcodeEAN13_Raw( ~toEncode) ] ; Let ( [ _foreground_color = Substitute ( "1111" ; "1" ; "AAAA" ) ; // set to black _background_color = Substitute ( "1111" ; "1" ; "////" ) ; // white _base = "Qk2sBAAAAAAAADYAAAAoAAAAfAEAAAEAAAABABgAAAAAAHYEAAASCwAAEgsAAAAAAAAAAAAA" ; _map = Substitute ( ~binary ; "0" ; _background_color ); _map = Substitute ( _map; "1" ; _foreground_color ); _map = _map & "////" ] ; If(Length(~binary) ≠ 95 ; "error" ; Base64Decode ( _base & _map ; "barcode.bmp" ) ) ) ) /** * ===================================== * BarcodeEAN13_Raw ( input ) * * PURPOSE: * Generates the binary number for the EAN13 barcode * * RETURNS: * A binary number representing the barcode * * PARAMETERS: * input: the number to encode * * RELEASE: * 2017-06-11 * * REFERENCES: * https://en.wikipedia.org/wiki/International_Article_Number * * VERSION: 1.0 * * AUTHOR: © 2015 Mc128k http://www.mc128k.com * ===================================== */ Let([ ~code = input; ~length = Length(Filter( ~code; "1234567890")) ]; If ( ~length ≠ 12 ; "length not 12: " & ~length & input; Let([ ~sum_Odd=Middle(~code; 2; 1) + Middle(~code; 4; 1) + Middle(~code; 6; 1) + Middle(~code; 8; 1) + Middle(~code; 10; 1) + Middle(~code; 12; 1); ~sum_Even=Middle(~code; 1; 1) + Middle(~code; 3; 1) + Middle(~code; 5; 1) + Middle(~code; 7; 1) + Middle(~code; 9; 1) + Middle(~code; 11; 1); ~checksum=Mod(10 - Mod(((3 * ~sum_Odd) + ~sum_Even) ; 10 ) ; 10); ~data=~code & ~checksum; ~EAN_Start="101"; ~EAN_Intermediate="01010"; ~EAN_Stop="101"; ~eval=Middle(~data; 1; 1); ~mask=Case ( ~eval=0 ; "000000" ; ~eval=1 ; "001011" ; ~eval=2 ; "001101" ; ~eval=3 ; "001110" ; ~eval=4 ; "010011" ; ~eval=5 ; "011001" ; ~eval=6 ; "011100" ; ~eval=7 ; "010101" ; ~eval=8 ; "010110" ; ~eval=9 ; "011010" ); ~p1_1= Let([~n = 1 ; ~d=Middle(~data;~n + 1;1)]; Case(~d=0 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001101" ; "0100111" ) ; ~d=1 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0011001" ; "0110011" ) ; ~d=2 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0010011" ; "0011011" ) ; ~d=3 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111101" ; "0100001" ) ; ~d=4 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0100011" ; "0011101" ) ; ~d=5 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110001" ; "0111001" ) ; ~d=6 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0101111" ; "0000101" ) ; ~d=7 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111011" ; "0010001" ) ; ~d=8 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110111" ; "0001001" ) ; ~d=9 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001011" ; "0010111" ) )); ~p1_2= Let([~n = 2 ; ~d=Middle(~data;~n + 1;1)]; Case(~d=0 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001101" ; "0100111" ) ; ~d=1 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0011001" ; "0110011" ) ; ~d=2 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0010011" ; "0011011" ) ; ~d=3 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111101" ; "0100001" ) ; ~d=4 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0100011" ; "0011101" ) ; ~d=5 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110001" ; "0111001" ) ; ~d=6 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0101111" ; "0000101" ) ; ~d=7 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111011" ; "0010001" ) ; ~d=8 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110111" ; "0001001" ) ; ~d=9 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001011" ; "0010111" ) )); ~p1_3= Let([~n = 3 ; ~d=Middle(~data;~n + 1;1)]; Case(~d=0 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001101" ; "0100111" ) ; ~d=1 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0011001" ; "0110011" ) ; ~d=2 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0010011" ; "0011011" ) ; ~d=3 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111101" ; "0100001" ) ; ~d=4 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0100011" ; "0011101" ) ; ~d=5 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110001" ; "0111001" ) ; ~d=6 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0101111" ; "0000101" ) ; ~d=7 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111011" ; "0010001" ) ; ~d=8 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110111" ; "0001001" ) ; ~d=9 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001011" ; "0010111" ) )); ~p1_4= Let([~n = 4 ; ~d=Middle(~data;~n + 1;1)]; Case(~d=0 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001101" ; "0100111" ) ; ~d=1 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0011001" ; "0110011" ) ; ~d=2 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0010011" ; "0011011" ) ; ~d=3 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111101" ; "0100001" ) ; ~d=4 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0100011" ; "0011101" ) ; ~d=5 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110001" ; "0111001" ) ; ~d=6 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0101111" ; "0000101" ) ; ~d=7 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111011" ; "0010001" ) ; ~d=8 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110111" ; "0001001" ) ; ~d=9 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001011" ; "0010111" ) )); ~p1_5= Let([~n = 5 ; ~d=Middle(~data;~n + 1;1)]; Case(~d=0 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001101" ; "0100111" ) ; ~d=1 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0011001" ; "0110011" ) ; ~d=2 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0010011" ; "0011011" ) ; ~d=3 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111101" ; "0100001" ) ; ~d=4 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0100011" ; "0011101" ) ; ~d=5 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110001" ; "0111001" ) ; ~d=6 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0101111" ; "0000101" ) ; ~d=7 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111011" ; "0010001" ) ; ~d=8 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110111" ; "0001001" ) ; ~d=9 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001011" ; "0010111" ) )); ~p1_6= Let([~n = 6 ; ~d=Middle(~data;~n + 1;1)]; Case(~d=0 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001101" ; "0100111" ) ; ~d=1 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0011001" ; "0110011" ) ; ~d=2 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0010011" ; "0011011" ) ; ~d=3 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111101" ; "0100001" ) ; ~d=4 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0100011" ; "0011101" ) ; ~d=5 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110001" ; "0111001" ) ; ~d=6 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0101111" ; "0000101" ) ; ~d=7 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0111011" ; "0010001" ) ; ~d=8 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0110111" ; "0001001" ) ; ~d=9 ; If ( Middle(~mask ; ~n ; 1) = 0 ; "0001011" ; "0010111" ) )); ~p2_1= Let([~n = 1 ; ~d=Middle(~data;~n + 1 + 6 ;1)]; Case(~d=0 ; "1110010" ; ~d=1 ; "1100110" ; ~d=2 ; "1101100" ; ~d=3 ; "1000010" ; ~d=4 ; "1011100" ; ~d=5 ; "1001110" ; ~d=6 ; "1010000" ; ~d=7 ; "1000100" ; ~d=8 ; "1001000" ; ~d=9 ; "1110100" )); ~p2_2= Let([~n = 2 ; ~d=Middle(~data;~n + 1 + 6 ;1)]; Case(~d=0 ; "1110010" ; ~d=1 ; "1100110" ; ~d=2 ; "1101100" ; ~d=3 ; "1000010" ; ~d=4 ; "1011100" ; ~d=5 ; "1001110" ; ~d=6 ; "1010000" ; ~d=7 ; "1000100" ; ~d=8 ; "1001000" ; ~d=9 ; "1110100" )); ~p2_3= Let([~n = 3 ; ~d=Middle(~data;~n + 1 + 6 ;1)]; Case(~d=0 ; "1110010" ; ~d=1 ; "1100110" ; ~d=2 ; "1101100" ; ~d=3 ; "1000010" ; ~d=4 ; "1011100" ; ~d=5 ; "1001110" ; ~d=6 ; "1010000" ; ~d=7 ; "1000100" ; ~d=8 ; "1001000" ; ~d=9 ; "1110100" )); ~p2_4= Let([~n = 4 ; ~d=Middle(~data;~n + 1 + 6 ;1)]; Case(~d=0 ; "1110010" ; ~d=1 ; "1100110" ; ~d=2 ; "1101100" ; ~d=3 ; "1000010" ; ~d=4 ; "1011100" ; ~d=5 ; "1001110" ; ~d=6 ; "1010000" ; ~d=7 ; "1000100" ; ~d=8 ; "1001000" ; ~d=9 ; "1110100" )); ~p2_5= Let([~n = 5 ; ~d=Middle(~data;~n + 1 + 6 ;1)]; Case(~d=0 ; "1110010" ; ~d=1 ; "1100110" ; ~d=2 ; "1101100" ; ~d=3 ; "1000010" ; ~d=4 ; "1011100" ; ~d=5 ; "1001110" ; ~d=6 ; "1010000" ; ~d=7 ; "1000100" ; ~d=8 ; "1001000" ; ~d=9 ; "1110100" )); ~p2_6= Let([~n = 6 ; ~d=Middle(~data;~n + 1 + 6 ;1)]; Case(~d=0 ; "1110010" ; ~d=1 ; "1100110" ; ~d=2 ; "1101100" ; ~d=3 ; "1000010" ; ~d=4 ; "1011100" ; ~d=5 ; "1001110" ; ~d=6 ; "1010000" ; ~d=7 ; "1000100" ; ~d=8 ; "1001000" ; ~d=9 ; "1110100" )); ~part_1=~p1_1 & ~p1_2 & ~p1_3 & ~p1_4 & ~p1_5 & ~p1_6; ~part_2=~p2_1 & ~p2_2 & ~p2_3 & ~p2_4 & ~p2_5 & ~p2_6 ]; ~EAN_Start & ~part_1 & ~EAN_Intermediate & ~part_2 & ~EAN_Stop ) // Let ) // If ~length ) // Let
Create an account or sign in to comment