Jump to content

Custom function for generating EAN-13


This topic is 2505 days old. Please don't post here. Open a new topic instead.

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
Link to comment
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.

Link to comment
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.

Link to comment
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.

Link to comment
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.

Link to comment
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

Link to comment
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.

Link to comment
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.

Link to comment
Share on other sites

  • 1 year later...
  • 1 month later...

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

 

Link to comment
Share on other sites

This topic is 2505 days old. Please don't post here. Open a new topic instead.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

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