diff --git a/FreeSansBold.ttf b/FreeSansBold.ttf new file mode 100644 index 0000000..e75685b Binary files /dev/null and b/FreeSansBold.ttf differ diff --git a/README.md b/README.md index 868a93b..2b933fc 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,34 @@ This is a re-working of [php-barcode](http://www.ashberg.de/php-barcode/ "php-ba Folke did a lot of good work, but it is nigh unreadable, I think, and I wanted to fix that. Because of this, too, it is really hard to add newer barcode types. +I also needed an on-the-fly barcode solution for a freelance job. This code has been in production for several years. + +### Requirements + +* PHP >= 5.4 +* [PHP GD](https://secure.php.net/manual/en/book.image.php) ### How do I use this? -Simply clone the repository and travel to ``/barcode.php``. You will get a random barcode. If you want a specific one, go to ``/barcode.php?code=`` and input a 12 digit number. +The signatures are fairly simple. Simply call `barcode` with the number of the +barcode and the scale of the barcode as an integer. The number needs to be +either 12 or 13 digits. The barcode will always be 13 digits even if only 12 +are supplied as the 13th digit is the checksum of the first 12. The barcode's +scale will not go lower than 2 for scaling reasons and no higher than 12 for +memory reasons. A scale of 4 has worked well for my purposes in the past. + + $barcode = new Barcode(1349875921348, 4); + $barcode = new Barcode(439457143245, 10); + +The key thing to keep up with is the `FreeSansBold.ttf` file. By default the +Barcode class will look into PHP's calling directory for the font file. You can +specify a path as a third parameter. + + $barcode = new Barcode(123456789120, 4, "/path/to/FreeSansBold.ttf"); + +There are only two public methods: `image()` and `display()`. The first +function `image()` returns the PHP created image as a reference. This may be +used to save an image to file, e.g. `imagepng($barcode->image(), +"/path/to/storage/barcode.png")`. The second function `display()` simply calls +the correct headers and displays the barcode in the browser. Useful for +debugging. diff --git a/barcode.php b/barcode.php index e4ed30a..37ddecd 100644 --- a/barcode.php +++ b/barcode.php @@ -1,38 +1,244 @@ "000000", 1 => "001011", 2 => "001101", 3 => "001110", + 4 => "010011", 5 => "011001", 6 => "011100", 7 => "010101", + 8 => "010110", 9 => "011010" + ); + + public static $LEFT_PARITY = array( + // Odd Encoding + 0 => array( + 0 => "0001101", 1 => "0011001", 2 => "0010011", 3 => "0111101", + 4 => "0100011", 5 => "0110001", 6 => "0101111", 7 => "0111011", + 8 => "0110111", 9 => "0001011" + ), + // Even Encoding + 1 => array ( + 0 => "0100111", 1 => "0110011", 2 => "0011011", 3 => "0100001", + 4 => "0011101", 5 => "0111001", 6 => "0000101", 7 => "0010001", + 8 => "0001001", 9 => "0010111" + ) + ); + + public static $RIGHT_PARITY = array( + 0 => "1110010", 1 => "1100110", 2 => "1101100", 3 => "1000010", + 4 => "1011100", 5 => "1001110", 6 => "1010000", 7 => "1000100", + 8 => "1001000", 9 => "1110100" + ); - function __construct($encoding, $number=null, $scale=null) + public static $GUARD = array( + 'start' => "101", 'middle' => "01010", 'end' => "101" + ); + + public static function checksum (string $ean) { + $even=true; $esum=0; $osum=0; + for ($i = strlen($ean)-1; $i >= 0; $i--) { + if ($even) $esum+=$ean[$i]; else $osum+=$ean[$i]; + $even=!$even; + } + return (10-((3*$esum+$osum)%10))%10; + } + + /** + * Create the barcode. $number is the 12/13 digit barcode to be displayed. + * The $scale is the scale of the image in integers. The scale will not go + * lower than 2 or greater than 12. + */ + + public function __construct (string $number, $scale, $fontpath=null) { - $this->number = ($number==null) ? $this->_random() : $number; - $this->scale = ($scale==null || $scale<4) ? 4 : $scale; + /* Get the parity key, which is based on the first digit. */ + $this->_key = self::$PARITY_KEY[substr($number,0,1)]; + + if (!$fontpath) + $this->font = dirname(__FILE__) . "/" . "FreeSansBold.ttf"; + else + $this->font = $fontpath; - // Reflection Class : Method + /* Clamp scale between 2 and 12 */ + if ($scale < 2) + $this->scale = 2; + else if ($scale > 12) + $this->scale = 12; + else + $this->scale = $scale; - $this->_encoder = new EAN13($this->number, $this->scale); + $len = strlen($number); + if ($len != 13 && $len != 12) + trigger_error('Barcode expects 12 or 13 digit number', E_USER_ERROR); + + /* The checksum (13th digit) can be calculated or supplied */ + $this->number = $number; + if ($len === 12) + $this->number .= self::checksum($number); + + $this->_bars = $this->_encode(); + $this->_createImage(); + $this->_drawBars(); + $this->_drawText(); } - function __destruct() + public function __destruct() { - $this->_encoder->display(); + imagedestroy($this->_image); } - private function _random() + /** + * The following incantations use the parity key (based off the + * first digit of the unencoded number) to encode the first six + * digits of the barcode. The last 6 use the same parity. + * + * So, if the key is 010101, the first digit (of the first six + * digits) uses odd parity encoding. The second uses even. The + * third uses odd, and so on. + */ + + protected function _encode() { - return substr(number_format(time() * rand(),0,'',''),0,12); + $barcode[] = self::$GUARD['start']; + for($i=1;$i<=strlen($this->number)-1;$i++) + { + if($i < 7) + $barcode[] = self::$LEFT_PARITY[$this->_key[$i-1]][substr($this->number, $i, 1)]; + else + $barcode[] = self::$RIGHT_PARITY[substr($this->number, $i, 1)]; + if($i == 6) + $barcode[] = self::$GUARD['middle']; + } + $barcode[] = self::$GUARD['end']; + return $barcode; } -} -$encoding = (isset($_GET['encoding'])) ? $_GET['encoding'] : 'EAN-13'; -$number = (isset($_GET['code'])) ? $_GET['code'] : null; -$scale = (isset($_GET['scale'])) ? $_GET['scale'] : null; + /** + * Create the image. + * + * The Height is 60 times the scale and the width is simply + * 180% of the height. + */ + + protected function _createImage() + { + $this->_height = $this->scale * 60; + $this->_width = 1.8 * $this->_height; + $this->_image = imagecreate($this->_width, $this->_height); + ImageColorAllocate($this->_image, 0xFF, 0xFF, 0xFF); + } + + /** + * Draw the actual bars themselves. + * + * We have defined some constants. MAX is the y-value for the maximum + * height a bar should go. FLOOR is the y-value for the minimum height. + * + * The differences in margin for MAX and FLOOR are because most of the + * barcode doesn't extend to the bottom, only the guards do. + * + * WIDTH is the actual width of the bars. + * + * X is the starting position of the bars, which is a fifth of the way + * into the image. + * + * To draw the bars, we translate a binary string into bars: + * + * 10111001 - bar, empty, bar, bar, bar, empty, empty, bar + */ + + protected function _drawBars() + { + $bar_color=ImageColorAllocate($this->_image, 0x00, 0x00, 0x00); + + $MAX = $this->_height*0.025; + $FLOOR = $this->_height*0.825; + $WIDTH = $this->scale; + + $x = ($this->_height * 0.2) - $WIDTH; + + foreach($this->_bars as $bar) + { + $tall = 0; + + if(strlen($bar)==3 || strlen($bar)==5) + $tall = ($this->_height * 0.15); + + for($i = 1; $i <= strlen($bar); $i++) + { + if(substr($bar, $i-1, 1)==='1') + imagefilledrectangle($this->_image, $x, $MAX, $x + $WIDTH, + $FLOOR + $tall, $bar_color); + $x += $WIDTH; + } + } + } + + /** + * Draw the text: + * + * The first digit is left of the first guard. The kerning + * is how much space is in between the individual characters. + * + * We add kerning after the first character to skip over the + * first guard. Then we do it again after the 6th character + * to skip over the second guard. + * + * We don't need to skip over the last guard. + * + * The fontsize is 7 times the scale. + * X is the start point, which is .05 a way into the image + */ + + protected function _drawText() + { + $x = $this->_width*0.05; + $y = $this->_height*0.96; + + $text_color=ImageColorAllocate($this->_image, 0x00, 0x00, 0x00); + + $fontsize = $this->scale*7; + $kerning = $fontsize*1; -new Barcode($encoding, $number, $scale); + for($i=0;$inumber);$i++) + { + imagettftext($this->_image, $fontsize, 0, $x, $y, $text_color, $this->font, $this->number[$i]); + if($i==0 || $i==6) + $x += $kerning*0.5; + $x += $kerning; + } + } + + /** + * Return the barcode's image by reference. + */ + + public function &image() + { + return $this->_image; + } + + /** + * Send the headers and display the barcode. + */ + + public function display() + { + header("Content-Type: image/png; name=\"barcode.png\""); + imagepng($this->_image); + } +} diff --git a/ean.php b/ean.php deleted file mode 100644 index 6b83b7a..0000000 --- a/ean.php +++ /dev/null @@ -1,173 +0,0 @@ -= 13 error - - // Get the parity key, which is based on the first digit. - $this->_key = $PARITY_KEY[substr($number,0,1)]; - - // The checksum is appended to the 12 digit string - $this->_checksum = ean_checksum($number); - $this->number = $number.$this->_checksum; - - $this->scale = $scale; - - $this->_bars = $this->_encode(); - $this->_createImage(); - $this->_drawBars(); - $this->_drawText(); - } - - /** - * The following incantations use the parity key (based off the - * first digit of the unencoded number) to encode the first six - * digits of the barcode. The last 6 use the same parity. - * - * So, if the key is 010101, the first digit (of the first six - * digits) uses odd parity encoding. The second uses even. The - * third uses odd, and so on. - */ - - protected function _encode() - { - global $LEFT_PARITY, $RIGHT_PARITY, $GUARD; - - $barcode[] = $GUARD['start']; - for($i=1;$i<=strlen($this->number)-1;$i++) - { - if($i<7) - $barcode[] = $LEFT_PARITY[$this->_key[$i-1]][substr($this->number, $i, 1)]; - else - $barcode[] = $RIGHT_PARITY[substr($this->number, $i, 1)]; - if($i==6) - $barcode[] = $GUARD['middle']; - } - $barcode[] = $GUARD['end']; - - return $barcode; - } - - /** - * Create the image. - * - * The Height is 60 times the scale and the width is simply - * 180% of the height. - */ - - protected function _createImage() - { - $this->_height = $this->scale*60; - $this->_width = 1.8*$this->_height; - - $this->_image = imagecreate($this->_width, $this->_height); - $bg_color=ImageColorAllocate($this->_image, 0xFF, 0xFF, 0xFF); - } - - /** - * Draw the actual bars themselves. - * - * We have defined some constants. MAX is the y-value for the maximum - * height a bar should go. FLOOR is the y-value for the minimum height. - * - * The differences in margin for MAX and FLOOR are because most of the - * barcode doesn't extend to the bottom, only the guards do. - * - * WIDTH is the actual width of the bars. - * - * X is the starting position of the bars, which is a fifth of the way - * into the image. - * - * To draw the bars, we translate a binary string into bars: - * - * 10111001 - bar, empty, bar, bar, bar, empty, empty, bar - */ - - protected function _drawBars() - { - $bar_color=ImageColorAllocate($this->_image, 0x00, 0x00, 0x00); - - define("MAX", $this->_height*0.025); - define("FLOOR", $this->_height*0.825); - define("WIDTH", $this->scale); - - $x = ($this->_height*0.2)-WIDTH; - - foreach($this->_bars as $bar) - { - $tall = 0; - - if(strlen($bar)==3 || strlen($bar)==5) - $tall = ($this->_height*0.15); - - for($i=1;$i<=strlen($bar);$i++) - { - if(substr($bar, $i-1, 1)==='1') - imagefilledrectangle($this->_image, $x, MAX, $x+WIDTH, FLOOR+$tall, $bar_color); - $x += WIDTH; - } - } - } - - /** - * Draw the text: - * - * The first digit is left of the first guard. The kerning - * is how much space is in between the individual characters. - * - * We add kerning after the first character to skip over the - * first guard. Then we do it again after the 6th character - * to skip over the second guard. - * - * We don't need to skip over the last guard. - * - * The fontsize is 7 times the scale. - * X is the start point, which is .05 a way into the image - */ - - protected function _drawText() - { - $x = $this->_width*0.05; - $y = $this->_height*0.96; - - $text_color=ImageColorAllocate($this->_image, 0x00, 0x00, 0x00); - - $font=dirname(__FILE__)."/"."FreeSansBold.ttf"; - $fontsize = $this->scale*(7); - $kerning = $fontsize*1; - - for($i=0;$inumber);$i++) - { - imagettftext($this->_image, $fontsize, 0, $x, $y, $text_color, $font, $this->number[$i]); - if($i==0 || $i==6) - $x += $kerning*0.5; - $x += $kerning; - } - } - - /** - * Send the headers and display the barcode. - * - * Destroy the image afterwards because we're firing and forgetting - */ - - public function display() - { - header("Content-Type: image/png; name=\"barcode.png\""); - imagepng($this->_image); - imagedestroy($this->_image); - } -} diff --git a/ean_parity.php b/ean_parity.php deleted file mode 100644 index eb854dc..0000000 --- a/ean_parity.php +++ /dev/null @@ -1,77 +0,0 @@ - "000000", - 1 => "001011", - 2 => "001101", - 3 => "001110", - 4 => "010011", - 5 => "011001", - 6 => "011100", - 7 => "010101", - 8 => "010110", - 9 => "011010" - ))); - -define ("LEFT_PARITY", serialize (array( - // Odd Encoding - 0 => array ( - 0 => "0001101", - 1 => "0011001", - 2 => "0010011", - 3 => "0111101", - 4 => "0100011", - 5 => "0110001", - 6 => "0101111", - 7 => "0111011", - 8 => "0110111", - 9 => "0001011" - ), - // Even Encoding - 1 => array ( - 0 => "0100111", - 1 => "0110011", - 2 => "0011011", - 3 => "0100001", - 4 => "0011101", - 5 => "0111001", - 6 => "0000101", - 7 => "0010001", - 8 => "0001001", - 9 => "0010111" - ) - ))); - -define ("RIGHT_PARITY", serialize (array( - 0 => "1110010", - 1 => "1100110", - 2 => "1101100", - 3 => "1000010", - 4 => "1011100", - 5 => "1001110", - 6 => "1010000", - 7 => "1000100", - 8 => "1001000", - 9 => "1110100" - ))); - -define ("GUARDS", serialize (array( - 'start' => "101", - 'middle' => "01010", - 'end' => "101", - ))); - -$PARITY_KEY = unserialize(PARITY_KEY); -$LEFT_PARITY = unserialize(LEFT_PARITY); -$RIGHT_PARITY = unserialize(RIGHT_PARITY); -$GUARD = unserialize(GUARDS); - -function ean_checksum($ean){ - $ean=(string)$ean; - $even=true; $esum=0; $osum=0; - for ($i=strlen($ean)-1;$i>=0;$i--){ - if ($even) $esum+=$ean[$i]; else $osum+=$ean[$i]; - $even=!$even; - } - return (10-((3*$esum+$osum)%10))%10; -}