From 42f4eb9c7bd1baffc5b54547ac96ad821d4c2179 Mon Sep 17 00:00:00 2001 From: jekkos-t520 Date: Mon, 9 Feb 2015 17:32:45 +0100 Subject: [PATCH] Add code128 as barcode_type --- application/libraries/Barcode_lib.php | 36 ++- application/views/barcodes/BarcodeBase.php | 178 ++++++++++++ application/views/barcodes/Code128.php | 323 +++++++++++++++++++++ application/views/barcodes/Code39.php | 188 ++++++++++++ 4 files changed, 717 insertions(+), 8 deletions(-) create mode 100644 application/views/barcodes/BarcodeBase.php create mode 100644 application/views/barcodes/Code128.php create mode 100644 application/views/barcodes/Code39.php diff --git a/application/libraries/Barcode_lib.php b/application/libraries/Barcode_lib.php index 9d7f83190..2e07a6d96 100644 --- a/application/libraries/Barcode_lib.php +++ b/application/libraries/Barcode_lib.php @@ -1,8 +1,14 @@ 'Code 39'); + var $supported_barcodes = array(1 => 'Code 39', 2 => 'Code 128'); function __construct() { @@ -16,6 +22,7 @@ class Barcode_lib function get_barcode_config() { + $data['barcode_type'] = $this->CI->Appconfig->get('barcode_type'); $data['barcode_font'] = $this->CI->Appconfig->get('barcode_font'); $data['barcode_font_size'] = $this->CI->Appconfig->get('barcode_font_size'); $data['barcode_height'] = $this->CI->Appconfig->get('barcode_height'); @@ -30,17 +37,30 @@ class Barcode_lib return $data; } + private function generate_barcode($item, $barcode_config) + { + if ($barcode_config['barcode_type'] == '1') + { + $barcode = new emberlabs\Barcode\Code39(); + } + else + { + $barcode = new emberlabs\Barcode\Code128(); + } + $barcode_content = $this->CI->Appconfig->get('barcode_content') === "id" ? $item['item_id'] : $item['item_number']; + $barcode->setData($barcode_content); + $barcode->setDimensions($barcode_config['barcode_width'], $barcode_config['barcode_height']); + $barcode->draw(); + return $barcode->base64(); + } + function create_display_barcode($item, $barcode_config) { + $display_table = ""; $display_table .= ""; - $display_table .= ""; + $barcode = $this->generate_barcode($item,$barcode_config); + $display_table .= ""; $display_table .= ""; $display_table .= ""; $display_table .= "
". $this->manage_display_layout($barcode_config['barcode_first_row'], $item, $barcode_config)."
". $this->manage_display_layout($barcode_config['barcode_second_row'], $item, $barcode_config)."
". $this->manage_display_layout($barcode_config['barcode_third_row'], $item, $barcode_config)."
"; diff --git a/application/views/barcodes/BarcodeBase.php b/application/views/barcodes/BarcodeBase.php new file mode 100644 index 000000000..83ee74ffd --- /dev/null +++ b/application/views/barcodes/BarcodeBase.php @@ -0,0 +1,178 @@ +x = (int) $x; + $this->y = (int) $y; + + return $this; + } + + /* + * Set Quality + * @param int q - jpeg quality + * @return instance of \emberlabs\Barcode\BarcodeBase + */ + public function setQuality($q) + { + $this->jpgQuality = (int) $q; + + return $this; + } + + /* + * Display human readable text below the code + * @param boolean enable - Enable the human readable text + * @return instance of \emberlabs\Barcode\BarcodeBase + */ + public function enableHumanText($enable = true) + { + $this->humanText = (boolean) $enable; + + return $this; + } + + /* + * Output Image to the buffer + * + * @return void + */ + public function output($type = 'png') + { + switch($type) + { + case 'jpg': + case 'jpeg': + imagejpeg($this->img, NULL, $this->jpgQuality); + break; + + case 'gif': + imagegif($this->img); + break; + + case 'png': + default: + imagepng($this->img); + break; + } + } + + /* + * Save Image + * + * @param string filename - File to write to (needs to have .png, .gif, or + * .jpg extension) + * @return void + * @throws \RuntimeException - If the file could not be written or some + * other I/O error. + */ + public function save($filename) + { + $type = strtolower(substr(strrchr($filename, '.'), 1)); + + switch($type) + { + case 'jpg': + case 'jpeg': + imagejpeg($this->img, $filename, $this->jpgQuality); + break; + + case 'gif': + imagegif($this->img, $filename); + break; + + case 'png': + imagepng($this->img, $filename); + break; + + default: + throw new \RuntimeException("Could not determine file type."); + break; + } + } + + /* + * Base64 Encoded + * For ouput in-page + * @return void + */ + public function base64() + { + ob_start(); + $this->output(); + + return base64_encode(ob_get_clean()); + } +} diff --git a/application/views/barcodes/Code128.php b/application/views/barcodes/Code128.php new file mode 100644 index 000000000..a721da0dd --- /dev/null +++ b/application/views/barcodes/Code128.php @@ -0,0 +1,323 @@ +', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', // 39 + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', // 49 + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', // 59 + '\\', ']', '^', '_', // 63 (We're going into the weird bytes next) + + // Hex is a little more concise in this context + "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", // 69 + "\x06", "\x07", "\x08", "\x09", "\x0A", "\x0B", // 75 + "\x0C", "\x0D", "\x0E", "\x0F", "\x10", "\x11", // 81 + "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", // 87 + "\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", // 93 + "\x1E", "\x1F", // 95 + + // Now for system codes + 'FNC_3', 'FNC_2', 'SHIFT_B', 'CODE_C', 'CODE_B', // 100 + 'FNC_4', 'FNC_1', 'START_A', 'START_B', 'START_C', // 105 + 'STOP', // 106 + ); + + /* + * Same idea from MapA applied here to B. + * @var static array + */ + private static $mapB = array( + ' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', // 9 (end) + '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', // 19 + '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', // 29 + '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', // 39 + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', // 49 + 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', // 59 + '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', // 69 + 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', // 79 + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', // 89 + 'z', '{', '|', '}', '~', "\x7F", // 95 + + // Now for system codes + 'FNC_3', 'FNC_2', 'SHIFT_A', 'CODE_C', 'FNC_4', // 100 + 'CODE_A', 'FNC_1', 'START_A', 'START_B', 'START_C', // 105 + 'STOP', // 106 + ); + + /* + * Map C works a little different. The index is the value when the mapping + * occors. + * @var static array + */ + private static $mapC = array( + 100 => + 'CODE_B', 'CODE_A', 'FNC_1', 'START_A', 'START_B', + 'START_C', 'STOP', // 106 + ); + + /* + * Subtypes + */ + const TYPE_AUTO = 0; // Automatically detect the best code + const TYPE_A = 1; // ASCII 00-95 (0-9, A-Z, Control codes, and some special chars) + const TYPE_B = 2; // ASCII 32-127 (0-9, A-Z, a-z, special chars) + const TYPE_C = 3; // Numbers 00-99 (two digits per code) + + /* + * Set the data + * + * @param mixed data - (int or string) Data to be encoded + * @return instance of \emberlabs\Barcode\BarcodeInterface + * @return throws \OverflowException + */ + public function setData($data) + { + $this->data = $data; + } + + /* + * Set the subtype + * Defaults to Autodetect + * @param int type - Const flag for the type + */ + public function setSubType($type) + { + $this->type = ($type < 1 || $type > 3) ? self::TYPE_AUTO : (int) $type; + } + + /* + * Get they key (value of the character) + * @return int - pattern + */ + private function getKey($char) + { + switch ($this->type) + { + case self::TYPE_A: + return array_search($char, self::$mapA); + break; + + case self::TYPE_B: + return array_search($char, self::$mapB); + break; + + case self::TYPE_C: + $charInt = (int) $char; + if (strlen($char) == 2 && $charInt <= 99 && $charInt >= 0) + { + return $charInt; + } + + return array_search($char, self::$mapC); + break; + + default: + $this->resolveSubtype(); + return $this->getKey($char); // recursion! + break; + } + } + + /* + * Get the bar + * @return int - pattern + */ + private function getBar($char) + { + $key = $this->getKey($char); + + return self::$barMap[($key !== false) ? $key : 0]; + } + + /* + * Resolve subtype + * @todo - Do some better charset checking and enforcement + * @return void + */ + private function resolveSubtype() + { + if ($this->type == self::TYPE_AUTO) + { + // If it is purely numeric, this is easy + if (is_numeric($this->data)) + { + $this->type = self::TYPE_C; + } + // Are there only capitals? + else if(strtoupper($this->data) == $this->data) + { + $this->type = self::TYPE_A; + } + else + { + $this->type = self::TYPE_B; + } + } + } + + /* + * Get the name of a start char fr te current subtype + * @return string + */ + private function getStartChar() + { + $this->resolveSubtype(); + + switch($this->type) + { + case self::TYPE_A: return 'START_A'; break; + case self::TYPE_B: return 'START_B'; break; + case self::TYPE_C: return 'START_C'; break; + } + } + + /* + * Draw the image + * + * @return void + */ + public function draw() + { + $this->resolveSubtype(); + $charAry = str_split($this->data); + + // Calc scaling + // Bars is in refrence to a single, 1-level bar + $numBarsRequired = ($this->type != self::TYPE_C) ? (sizeof($charAry) * 11) + 35 : ((sizeof($charAry)/2) * 11) + 35; + $this->x = ($this->x == 0) ? $numBarsRequired : $this->x; + $pxPerBar = (int) ($this->x / $numBarsRequired); + $currentX = ($this->x - ($numBarsRequired * $pxPerBar)) / 2; + + if ($pxPerBar < 1) + { + throw new \LogicException("Not enough space on this barcode for this message, increase the width of the barcode"); + } + + if ($this->type == self::TYPE_C) + { + if (sizeof($charAry) % 2) + { + array_unshift($charAry, '0'); + } + + $pairs = ''; + $newAry = array(); + foreach($charAry as $k => $char) + { + if (($k % 2) == 0 && $k != 0) + { + $newAry[] = $pairs; + $pairs = ''; + } + + $pairs .= $char; + } + + $newAry[] = $pairs; + $charAry = $newAry; + } + + // Add the start + array_unshift($charAry, $this->getStartChar()); + + // Checksum collector + $checkSumCollector = $this->getKey($this->getStartChar()); + + $this->img = @imagecreate($this->x, $this->y); + $white = imagecolorallocate($this->img, 255, 255, 255); + $black = imagecolorallocate($this->img, 0, 0, 0); + + // Print the code + foreach($charAry as $k => $char) + { + $code = $this->getBar($char); + $checkSumCollector += $this->getKey($char) * $k; // $k will be 0 for our first + + foreach(str_split((string) $code) as $bit) + { + imagefilledrectangle($this->img, $currentX, 0, ($currentX + $pxPerBar), ($this->y - 1), (($bit == '1') ? $black : $white)); + $currentX += $pxPerBar; + } + } + + $ending[] = self::$barMap[$checkSumCollector % 103]; + $ending[] = self::$barMap[106]; // STOP. + + foreach($ending as $code) + { + foreach(str_split((string) $code) as $bit) + { + imagefilledrectangle($this->img, $currentX, 0, ($currentX + $pxPerBar), ($this->y - 1), (($bit == '1') ? $black : $white)); + $currentX += $pxPerBar; + } + } + } +} diff --git a/application/views/barcodes/Code39.php b/application/views/barcodes/Code39.php new file mode 100644 index 000000000..18418832a --- /dev/null +++ b/application/views/barcodes/Code39.php @@ -0,0 +1,188 @@ + '011000100', + '$' => '010101000', + '%' => '000101010', + '*' => '010010100', // Start/stop marker + '+' => '010001010', + '|' => '010000101', + '.' => '110000100', + '/' => '010100010', + '-' => '010000101', + '0' => '000110100', + '1' => '100100001', + '2' => '001100001', + '3' => '101100000', + '4' => '000110001', + '5' => '100110000', + '6' => '001110000', + '7' => '000100101', + '8' => '100100100', + '9' => '001100100', + 'A' => '100001001', + 'B' => '001001001', + 'C' => '101001000', + 'D' => '000011001', + 'E' => '100011000', + 'F' => '001011000', + 'G' => '000001101', + 'H' => '100001100', + 'I' => '001001100', + 'J' => '000011100', + 'K' => '100000011', + 'L' => '001000011', + 'M' => '101000010', + 'N' => '000010011', + 'O' => '100010010', + 'P' => '001010010', + 'Q' => '000000111', + 'R' => '100000110', + 'S' => '001000110', + 'T' => '000010110', + 'U' => '110000001', + 'V' => '011000001', + 'W' => '111000000', + 'X' => '010010001', + 'Y' => '110010000', + 'Z' => '011010000', + ); + + /* + * const bar proportions + */ + const NARROW_BAR = 20; + const WIDE_BAR = 55; + const QUIET_BAR = 35; + + /* + * Set the data + * + * @param mixed data - (int or string) Data to be encoded + * @return instance of \emberlabs\Barcode\BarcodeInterface + * @return throws \OverflowException + */ + public function setData($data) + { + // I know, lots of junk. + $this->data = '*' . strtoupper(ltrim(rtrim(trim($data), '*'), '*')) . '*'; + } + + /* + * Get a binary map value + */ + private function getMap($char) + { + return self::$binMap[$char] ?: self::$this->binMap[' ']; + } + + /* + * Draw the image + * + * Based on the implentation PHP Barcode Image Generator v1.0 + * by Charles J. Scheffold - cs@sid6581.net + * It was released into the Public Domain by its creator. + * + * @return void + */ + public function draw() + { + $this->img = @imagecreate($this->x, $this->y); + + if (!$this->img) + { + throw new \RuntimeException("Code39: Image failed to initialize"); + } + + // Length of data X [ 6 narrow bars + 3 wide bars + A single Quiet stop ] - a single quiet stop + $pxPerChar = (strlen($this->data) * ((6 * self::NARROW_BAR) + (3 * self::WIDE_BAR) + self::QUIET_BAR)) - self::QUIET_BAR; + $widthQuotient = $this->x / $pxPerChar; + + // Lengths per type + $narrowBar = (int) (self::NARROW_BAR * $widthQuotient); + $wideBar = (int) (self::WIDE_BAR * $widthQuotient); + $quietBar = (int) (self::QUIET_BAR * $widthQuotient); + + + $imageWidth = (strlen($this->data) * ((6 * $narrowBar) + (3 * $wideBar) + $quietBar)) - $quietBar; + + // Do we have degenerate rectangles? + if ($narrowBar < 1 || $wideBar < 1 || $quietBar < 1 || $narrowBar == $quietBar || $narrowBar == $wideBar || $wideBar == $quietBar) + { + throw new \OverflowException("You need to spcify a bigger width to properly display this barcode"); + } + + $currentBarX = (int)(($this->x - $imageWidth) / 2); + $charAry = str_split($this->data); + + // Grab our colors + $white = imagecolorallocate($this->img, 255, 255, 255); + $black = imagecolorallocate($this->img, 0, 0, 0); + $color = $black; + + foreach($charAry as $_k => $char) + { + $code = str_split($this->getMap($char)); + $color = $black; + + foreach($code as $k => $bit) + { + // Narrow bar + if ($bit == '0') + { + imagefilledrectangle($this->img, $currentBarX, 0, ($currentBarX + $narrowBar), ($this->y - 1), $color); + $currentBarX += $narrowBar; + } + // Wide Bar + else if($bit == '1') + { + imagefilledrectangle($this->img, $currentBarX, 0, ($currentBarX + $wideBar), ($this->y - 1), $color); + $currentBarX += $wideBar; + } + + $color = ($color == $black) ? $white : $black; + } + + // Skip the spacer on the last run + if ($_k == (sizeof($charAry) - 1)) + { + break; + } + + // Draw spacer + imagefilledrectangle($this->img, $currentBarX, 0, ($currentBarX + $quietBar), ($this->y - 1), $white); + $currentBarX += $quietBar; + } + } +}