Jump to content
Sign in to follow this  
Mc128k

Custom function for generating EAN-13

Recommended Posts

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.

 

  • Like 1

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.