', '?', '@', '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? elseif(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 reference 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); if (!$this->img) { throw new \RuntimeException("Code128: Image failed to initialize"); } $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; } } } } ?>