├── BarcodeBase.php ├── Code128.php ├── Code39.php ├── DataMatrix.php ├── QRCode.php ├── UPC.php ├── license.txt └── readme.md /BarcodeBase.php: -------------------------------------------------------------------------------- 1 | x = (int) $x; 78 | $this->y = (int) $y; 79 | 80 | return $this; 81 | } 82 | 83 | /* 84 | * Set Quality 85 | * @param int q - jpeg quality 86 | * @return instance of \emberlabs\Barcode\BarcodeBase 87 | */ 88 | public function setQuality($q) 89 | { 90 | $this->jpgQuality = (int) $q; 91 | 92 | return $this; 93 | } 94 | 95 | /* 96 | * Display human readable text below the code 97 | * @param boolean enable - Enable the human readable text 98 | * @return instance of \emberlabs\Barcode\BarcodeBase 99 | */ 100 | public function enableHumanText($enable = true) 101 | { 102 | $this->humanText = (boolean) $enable; 103 | 104 | return $this; 105 | } 106 | 107 | /* 108 | * Output Image to the buffer 109 | * 110 | * @return void 111 | */ 112 | public function output($type = 'png') 113 | { 114 | switch($type) 115 | { 116 | case 'jpg': 117 | case 'jpeg': 118 | imagejpeg($this->img, NULL, $this->jpgQuality); 119 | break; 120 | 121 | case 'gif': 122 | imagegif($this->img); 123 | break; 124 | 125 | case 'png': 126 | default: 127 | imagepng($this->img); 128 | break; 129 | } 130 | } 131 | 132 | /* 133 | * Save Image 134 | * 135 | * @param string filename - File to write to (needs to have .png, .gif, or 136 | * .jpg extension) 137 | * @return void 138 | * @throws \RuntimeException - If the file could not be written or some 139 | * other I/O error. 140 | */ 141 | public function save($filename) 142 | { 143 | $type = strtolower(substr(strrchr($filename, '.'), 1)); 144 | 145 | switch($type) 146 | { 147 | case 'jpg': 148 | case 'jpeg': 149 | imagejpeg($this->img, $filename, $this->jpgQuality); 150 | break; 151 | 152 | case 'gif': 153 | imagegif($this->img, $filename); 154 | break; 155 | 156 | case 'png': 157 | imagepng($this->img, $filename); 158 | break; 159 | 160 | default: 161 | throw new \RuntimeException("Could not determine file type."); 162 | break; 163 | } 164 | } 165 | 166 | /* 167 | * Base64 Encoded 168 | * For ouput in-page 169 | * @return void 170 | */ 171 | public function base64() 172 | { 173 | ob_start(); 174 | $this->output(); 175 | 176 | return base64_encode(ob_get_clean()); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /Code128.php: -------------------------------------------------------------------------------- 1 | ', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', // 39 75 | 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', // 49 76 | 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', // 59 77 | '\\', ']', '^', '_', // 63 (We're going into the weird bytes next) 78 | 79 | // Hex is a little more concise in this context 80 | "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", // 69 81 | "\x06", "\x07", "\x08", "\x09", "\x0A", "\x0B", // 75 82 | "\x0C", "\x0D", "\x0E", "\x0F", "\x10", "\x11", // 81 83 | "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", // 87 84 | "\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", // 93 85 | "\x1E", "\x1F", // 95 86 | 87 | // Now for system codes 88 | 'FNC_3', 'FNC_2', 'SHIFT_B', 'CODE_C', 'CODE_B', // 100 89 | 'FNC_4', 'FNC_1', 'START_A', 'START_B', 'START_C', // 105 90 | 'STOP', // 106 91 | ); 92 | 93 | /* 94 | * Same idea from MapA applied here to B. 95 | * @var static array 96 | */ 97 | private static $mapB = array( 98 | ' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', // 9 (end) 99 | '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', // 19 100 | '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', // 29 101 | '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', // 39 102 | 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', // 49 103 | 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', // 59 104 | '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', // 69 105 | 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', // 79 106 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', // 89 107 | 'z', '{', '|', '}', '~', "\x7F", // 95 108 | 109 | // Now for system codes 110 | 'FNC_3', 'FNC_2', 'SHIFT_A', 'CODE_C', 'FNC_4', // 100 111 | 'CODE_A', 'FNC_1', 'START_A', 'START_B', 'START_C', // 105 112 | 'STOP', // 106 113 | ); 114 | 115 | /* 116 | * Map C works a little different. The index is the value when the mapping 117 | * occors. 118 | * @var static array 119 | */ 120 | private static $mapC = array( 121 | 100 => 122 | 'CODE_B', 'CODE_A', 'FNC_1', 'START_A', 'START_B', 123 | 'START_C', 'STOP', // 106 124 | ); 125 | 126 | /* 127 | * Subtypes 128 | */ 129 | const TYPE_AUTO = 0; // Automatically detect the best code 130 | const TYPE_A = 1; // ASCII 00-95 (0-9, A-Z, Control codes, and some special chars) 131 | const TYPE_B = 2; // ASCII 32-127 (0-9, A-Z, a-z, special chars) 132 | const TYPE_C = 3; // Numbers 00-99 (two digits per code) 133 | 134 | /* 135 | * Set the data 136 | * 137 | * @param mixed data - (int or string) Data to be encoded 138 | * @return instance of \emberlabs\Barcode\BarcodeInterface 139 | * @return throws \OverflowException 140 | */ 141 | public function setData($data) 142 | { 143 | $this->data = $data; 144 | } 145 | 146 | /* 147 | * Set the subtype 148 | * Defaults to Autodetect 149 | * @param int type - Const flag for the type 150 | */ 151 | public function setSubType($type) 152 | { 153 | $this->type = ($type < 1 || $type > 3) ? self::TYPE_AUTO : (int) $type; 154 | } 155 | 156 | /* 157 | * Get they key (value of the character) 158 | * @return int - pattern 159 | */ 160 | private function getKey($char) 161 | { 162 | switch ($this->type) 163 | { 164 | case self::TYPE_A: 165 | return array_search($char, self::$mapA); 166 | break; 167 | 168 | case self::TYPE_B: 169 | return array_search($char, self::$mapB); 170 | break; 171 | 172 | case self::TYPE_C: 173 | $charInt = (int) $char; 174 | if (strlen($char) == 2 && $charInt <= 99 && $charInt >= 0) 175 | { 176 | return $charInt; 177 | } 178 | 179 | return array_search($char, self::$mapC); 180 | break; 181 | 182 | default: 183 | $this->resolveSubtype(); 184 | return $this->getKey($char); // recursion! 185 | break; 186 | } 187 | } 188 | 189 | /* 190 | * Get the bar 191 | * @return int - pattern 192 | */ 193 | private function getBar($char) 194 | { 195 | $key = $this->getKey($char); 196 | 197 | return self::$barMap[($key !== false) ? $key : 0]; 198 | } 199 | 200 | /* 201 | * Resolve subtype 202 | * @todo - Do some better charset checking and enforcement 203 | * @return void 204 | */ 205 | private function resolveSubtype() 206 | { 207 | if ($this->type == self::TYPE_AUTO) 208 | { 209 | // If it is purely numeric, this is easy 210 | if (is_numeric($this->data)) 211 | { 212 | $this->type = self::TYPE_C; 213 | } 214 | // Are there only capitals? 215 | else if(strtoupper($this->data) == $this->data) 216 | { 217 | $this->type = self::TYPE_A; 218 | } 219 | else 220 | { 221 | $this->type = self::TYPE_B; 222 | } 223 | } 224 | } 225 | 226 | /* 227 | * Get the name of a start char fr te current subtype 228 | * @return string 229 | */ 230 | private function getStartChar() 231 | { 232 | $this->resolveSubtype(); 233 | 234 | switch($this->type) 235 | { 236 | case self::TYPE_A: return 'START_A'; break; 237 | case self::TYPE_B: return 'START_B'; break; 238 | case self::TYPE_C: return 'START_C'; break; 239 | } 240 | } 241 | 242 | /* 243 | * Draw the image 244 | * 245 | * @return void 246 | */ 247 | public function draw() 248 | { 249 | $this->resolveSubtype(); 250 | $charAry = str_split($this->data); 251 | 252 | // Calc scaling 253 | // Bars is in refrence to a single, 1-level bar 254 | $numBarsRequired = ($this->type != self::TYPE_C) ? (sizeof($charAry) * 11) + 35 : ((sizeof($charAry)/2) * 11) + 35; 255 | $pxPerBar = (int) ($this->x / $numBarsRequired); 256 | $currentX = ($this->x - ($numBarsRequired * $pxPerBar)) / 2; 257 | 258 | if ($pxPerBar < 1) 259 | { 260 | throw new LogicException("Not enough space on this barcode for this message, increase the width of the barcode"); 261 | } 262 | 263 | if ($this->type == self::TYPE_C) 264 | { 265 | if (sizeof($charAry) % 2) 266 | { 267 | array_unshift($charAry, '0'); 268 | } 269 | 270 | $pairs = ''; 271 | $newAry = array(); 272 | foreach($charAry as $k => $char) 273 | { 274 | if (($k % 2) == 0 && $k != 0) 275 | { 276 | $newAry[] = $pairs; 277 | $pairs = ''; 278 | } 279 | 280 | $pairs .= $char; 281 | } 282 | 283 | $newAry[] = $pairs; 284 | $charAry = $newAry; 285 | } 286 | 287 | // Add the start 288 | array_unshift($charAry, $this->getStartChar()); 289 | 290 | // Checksum collector 291 | $checkSumCollector = $this->getKey($this->getStartChar()); 292 | 293 | $this->img = @imagecreate($this->x, $this->y); 294 | $white = imagecolorallocate($this->img, 255, 255, 255); 295 | $black = imagecolorallocate($this->img, 0, 0, 0); 296 | 297 | // Print the code 298 | foreach($charAry as $k => $char) 299 | { 300 | $code = $this->getBar($char); 301 | $checkSumCollector += $this->getKey($char) * $k; // $k will be 0 for our first 302 | 303 | foreach(str_split((string) $code) as $bit) 304 | { 305 | imagefilledrectangle($this->img, $currentX, 0, ($currentX + $pxPerBar), ($this->y - 1), (($bit == '1') ? $black : $white)); 306 | $currentX += $pxPerBar; 307 | } 308 | } 309 | 310 | $ending[] = self::$barMap[$checkSumCollector % 103]; 311 | $ending[] = self::$barMap[106]; // STOP. 312 | 313 | foreach($ending as $code) 314 | { 315 | foreach(str_split((string) $code) as $bit) 316 | { 317 | imagefilledrectangle($this->img, $currentX, 0, ($currentX + $pxPerBar), ($this->y - 1), (($bit == '1') ? $black : $white)); 318 | $currentX += $pxPerBar; 319 | } 320 | } 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /Code39.php: -------------------------------------------------------------------------------- 1 | '011000100', 36 | '$' => '010101000', 37 | '%' => '000101010', 38 | '*' => '010010100', // Start/stop marker 39 | '+' => '010001010', 40 | '|' => '010000101', 41 | '.' => '110000100', 42 | '/' => '010100010', 43 | '-' => '010000101', 44 | '0' => '000110100', 45 | '1' => '100100001', 46 | '2' => '001100001', 47 | '3' => '101100000', 48 | '4' => '000110001', 49 | '5' => '100110000', 50 | '6' => '001110000', 51 | '7' => '000100101', 52 | '8' => '100100100', 53 | '9' => '001100100', 54 | 'A' => '100001001', 55 | 'B' => '001001001', 56 | 'C' => '101001000', 57 | 'D' => '000011001', 58 | 'E' => '100011000', 59 | 'F' => '001011000', 60 | 'G' => '000001101', 61 | 'H' => '100001100', 62 | 'I' => '001001100', 63 | 'J' => '000011100', 64 | 'K' => '100000011', 65 | 'L' => '001000011', 66 | 'M' => '101000010', 67 | 'N' => '000010011', 68 | 'O' => '100010010', 69 | 'P' => '001010010', 70 | 'Q' => '000000111', 71 | 'R' => '100000110', 72 | 'S' => '001000110', 73 | 'T' => '000010110', 74 | 'U' => '110000001', 75 | 'V' => '011000001', 76 | 'W' => '111000000', 77 | 'X' => '010010001', 78 | 'Y' => '110010000', 79 | 'Z' => '011010000', 80 | ); 81 | 82 | /* 83 | * const bar proportions 84 | */ 85 | const NARROW_BAR = 20; 86 | const WIDE_BAR = 55; 87 | const QUIET_BAR = 35; 88 | 89 | /* 90 | * Set the data 91 | * 92 | * @param mixed data - (int or string) Data to be encoded 93 | * @return instance of \emberlabs\Barcode\BarcodeInterface 94 | * @return throws \OverflowException 95 | */ 96 | public function setData($data) 97 | { 98 | // I know, lots of junk. 99 | $this->data = '*' . strtoupper(ltrim(rtrim(trim($data), '*'), '*')) . '*'; 100 | } 101 | 102 | /* 103 | * Get a binary map value 104 | */ 105 | private function getMap($char) 106 | { 107 | return self::$binMap[$char] ?: self::$this->binMap[' ']; 108 | } 109 | 110 | /* 111 | * Draw the image 112 | * 113 | * Based on the implentation PHP Barcode Image Generator v1.0 114 | * by Charles J. Scheffold - cs@sid6581.net 115 | * It was released into the Public Domain by its creator. 116 | * 117 | * @return void 118 | */ 119 | public function draw() 120 | { 121 | $this->img = @imagecreate($this->x, $this->y); 122 | 123 | if (!$this->img) 124 | { 125 | throw new \RuntimeException("Code39: Image failed to initialize"); 126 | } 127 | 128 | // Length of data X [ 6 narrow bars + 3 wide bars + A single Quiet stop ] - a single quiet stop 129 | $pxPerChar = (strlen($this->data) * ((6 * self::NARROW_BAR) + (3 * self::WIDE_BAR) + self::QUIET_BAR)) - self::QUIET_BAR; 130 | $widthQuotient = $this->x / $pxPerChar; 131 | 132 | // Lengths per type 133 | $narrowBar = (int) (self::NARROW_BAR * $widthQuotient); 134 | $wideBar = (int) (self::WIDE_BAR * $widthQuotient); 135 | $quietBar = (int) (self::QUIET_BAR * $widthQuotient); 136 | 137 | 138 | $imageWidth = (strlen($this->data) * ((6 * $narrowBar) + (3 * $wideBar) + $quietBar)) - $quietBar; 139 | 140 | // Do we have degenerate rectangles? 141 | if ($narrowBar < 1 || $wideBar < 1 || $quietBar < 1 || $narrowBar == $quietBar || $narrowBar == $wideBar || $wideBar == $quietBar) 142 | { 143 | throw new \OverflowException("You need to spcify a bigger width to properly display this barcode"); 144 | } 145 | 146 | $currentBarX = (int)(($this->x - $imageWidth) / 2); 147 | $charAry = str_split($this->data); 148 | 149 | // Grab our colors 150 | $white = imagecolorallocate($this->img, 255, 255, 255); 151 | $black = imagecolorallocate($this->img, 0, 0, 0); 152 | $color = $black; 153 | 154 | foreach($charAry as $_k => $char) 155 | { 156 | $code = str_split($this->getMap($char)); 157 | $color = $black; 158 | 159 | foreach($code as $k => $bit) 160 | { 161 | // Narrow bar 162 | if ($bit == '0') 163 | { 164 | imagefilledrectangle($this->img, $currentBarX, 0, ($currentBarX + $narrowBar), ($this->y - 1), $color); 165 | $currentBarX += $narrowBar; 166 | } 167 | // Wide Bar 168 | else if($bit == '1') 169 | { 170 | imagefilledrectangle($this->img, $currentBarX, 0, ($currentBarX + $wideBar), ($this->y - 1), $color); 171 | $currentBarX += $wideBar; 172 | } 173 | 174 | $color = ($color == $black) ? $white : $black; 175 | } 176 | 177 | // Skip the spacer on the last run 178 | if ($_k == (sizeof($charAry) - 1)) 179 | { 180 | break; 181 | } 182 | 183 | // Draw spacer 184 | imagefilledrectangle($this->img, $currentBarX, 0, ($currentBarX + $quietBar), ($this->y - 1), $white); 185 | $currentBarX += $quietBar; 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /DataMatrix.php: -------------------------------------------------------------------------------- 1 |