├── LICENSE.md ├── composer.json ├── config └── barcode.php └── src ├── AbstractGenerator.php ├── Barcode.php ├── BarcodeBar.php ├── BarcodeManager.php ├── BarcodeServiceProvider.php ├── Contracts ├── Factory.php └── ImageType.php ├── Drivers ├── DynamicHTML.php ├── HTML.php ├── JPG.php ├── PNG.php └── SVG.php ├── Enums ├── BarcodeType.php └── Type.php ├── Exceptions ├── BarcodeException.php ├── InvalidCharacterException.php ├── InvalidCheckDigitException.php ├── InvalidFormatException.php ├── InvalidLengthException.php └── UnknownTypeException.php ├── Facades └── Barcode.php ├── Helpers ├── BarcodeHelper.php └── BinarySequenceConverter.php └── Types ├── TypeCodabar.php ├── TypeCode11.php ├── TypeCode128.php ├── TypeCode128A.php ├── TypeCode128B.php ├── TypeCode128C.php ├── TypeCode32.php ├── TypeCode39.php ├── TypeCode39Checksum.php ├── TypeCode39Extended.php ├── TypeCode39ExtendedChecksum.php ├── TypeCode93.php ├── TypeEan13.php ├── TypeEan8.php ├── TypeEanUpcBase.php ├── TypeIntelligentMailBarcode.php ├── TypeInterface.php ├── TypeInterleaved25.php ├── TypeInterleaved25Checksum.php ├── TypeKix.php ├── TypeMsi.php ├── TypeMsiChecksum.php ├── TypePharmacode.php ├── TypePharmacodeTwoCode.php ├── TypePlanet.php ├── TypePostnet.php ├── TypeRms4cc.php ├── TypeStandard2of5.php ├── TypeStandard2of5Checksum.php ├── TypeUpcA.php ├── TypeUpcE.php ├── TypeUpcExtension2.php └── TypeUpcExtension5.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) AgeekDev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ageekdev/laravel-barcode", 3 | "description": "laravel barcode generator", 4 | "keywords": [ 5 | "ageekdev", 6 | "laravel", 7 | "barcode" 8 | ], 9 | "homepage": "https://github.com/ageekdev/laravel-barcode", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Tint Naing Win", 14 | "email": "amigo.k8@gmail.com", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.1", 20 | "ext-bcmath": "*", 21 | "illuminate/contracts": "^9.0|^10.0|^11.0|^12.0", 22 | "spatie/color": "^1.5" 23 | }, 24 | "require-dev": { 25 | "laravel/pint": "^1.5", 26 | "larastan/larastan": "^2.0|^3.0", 27 | "orchestra/testbench": "^7.31|^8.11|^9.0|^10.0", 28 | "pestphp/pest": "^1.21|^2.0|^3.0", 29 | "pestphp/pest-plugin-laravel": "^1.4|^2.0|^3.0", 30 | "phpstan/extension-installer": "^1.1", 31 | "phpstan/phpstan-deprecation-rules": "^1.0|^2.0", 32 | "phpstan/phpstan-phpunit": "^1.0|^2.0", 33 | "roave/security-advisories": "dev-latest" 34 | }, 35 | "autoload": { 36 | "psr-4": { 37 | "AgeekDev\\Barcode\\": "src" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "AgeekDev\\Barcode\\Tests\\": "tests" 43 | } 44 | }, 45 | "suggest": { 46 | "ext-gd": "For JPG and PNG generators, GD or Imagick is required", 47 | "ext-imagick": "For JPG and PNG generators, GD or Imagick is required" 48 | }, 49 | "scripts": { 50 | "analyse": "vendor/bin/phpstan analyse", 51 | "test": "vendor/bin/pest", 52 | "test-coverage": "vendor/bin/pest --coverage", 53 | "format": "vendor/bin/pint" 54 | }, 55 | "config": { 56 | "sort-packages": true, 57 | "allow-plugins": { 58 | "pestphp/pest-plugin": true, 59 | "phpstan/extension-installer": true 60 | } 61 | }, 62 | "extra": { 63 | "laravel": { 64 | "providers": [ 65 | "AgeekDev\\Barcode\\BarcodeServiceProvider" 66 | ], 67 | "aliases": { 68 | "Barcode": "AgeekDev\\Barcode\\Facades\\Barcode" 69 | } 70 | } 71 | }, 72 | "minimum-stability": "dev", 73 | "prefer-stable": true 74 | } 75 | -------------------------------------------------------------------------------- /config/barcode.php: -------------------------------------------------------------------------------- 1 | env('BARCODE_DRIVER', 'png'), 17 | 18 | /* 19 | |---------------------------------------------------------------------------------------------- 20 | | Accepted Barcode Types 21 | |---------------------------------------------------------------------------------------------- 22 | | Supported: TYPE_CODE_32, TYPE_CODE_39, TYPE_CODE_39_CHECKSUM, TYPE_CODE_39E, 23 | | TYPE_CODE_39E_CHECKSUM, TYPE_CODE_93, TYPE_STANDARD_2_5, TYPE_STANDARD_2_5_CHECKSUM, 24 | | TYPE_INTERLEAVED_2_5, TYPE_INTERLEAVED_2_5_CHECKSUM, TYPE_CODE_128, TYPE_CODE_128_A, 25 | | TYPE_CODE_128_B, TYPE_CODE_128_C, TYPE_EAN_2, TYPE_EAN_5, TYPE_EAN_8, TYPE_EAN_13, 26 | | TYPE_UPC_A, TYPE_UPC_E, TYPE_MSI, TYPE_MSI_CHECKSUM, TYPE_POSTNET, TYPE_PLANET, TYPE_RMS4CC, 27 | | TYPE_KIX, TYPE_IMB, TYPE_CODABAR, TYPE_CODE_11, TYPE_PHARMA_CODE, TYPE_PHARMA_CODE_TWO_TRACKS 28 | | 29 | */ 30 | 'type' => \AgeekDev\Barcode\Enums\BarcodeType::CODE_128, 31 | 32 | /* 33 | * Foreground color of the barcode 34 | */ 35 | 'foreground_color' => '#000000', 36 | 37 | /* 38 | * width factor of barcode 39 | */ 40 | 'width_factor' => 2, 41 | 42 | /* 43 | * Height of image barcode 44 | */ 45 | 'height' => 30, 46 | 47 | ]; 48 | -------------------------------------------------------------------------------- /src/AbstractGenerator.php: -------------------------------------------------------------------------------- 1 | foregroundColor = config('barcode.foreground_color'); 26 | $this->widthFactor = config('barcode.width_factor'); 27 | $this->height = config('barcode.height'); 28 | $this->type = config('barcode.type'); 29 | } 30 | 31 | protected function getBarcodeData(string $code, BarcodeType|Type $type): Barcode 32 | { 33 | return $this->createDataBuilderForType($type)->getBarcodeData($code); 34 | } 35 | 36 | protected function createDataBuilderForType(BarcodeType|Type $type): TypeInterface 37 | { 38 | return $type->class(); 39 | } 40 | 41 | public function type(BarcodeType|Type $type): static 42 | { 43 | $this->type = $type; 44 | 45 | return $this; 46 | } 47 | 48 | public function foregroundColor(string $foregroundColor): static 49 | { 50 | $this->foregroundColor = $foregroundColor; 51 | 52 | return $this; 53 | } 54 | 55 | public function height(int $height): static 56 | { 57 | $this->height = $height; 58 | 59 | return $this; 60 | } 61 | 62 | public function widthFactor(int $widthFactor): static 63 | { 64 | $this->widthFactor = $widthFactor; 65 | 66 | return $this; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Barcode.php: -------------------------------------------------------------------------------- 1 | barcode = $barcode; 18 | } 19 | 20 | /** 21 | * Add bar. 22 | */ 23 | public function addBar(BarcodeBar $bar): void 24 | { 25 | $this->bars[] = $bar; 26 | $this->width += $bar->getWidth(); 27 | $this->height = max($this->height, $bar->getHeight()); 28 | } 29 | 30 | /** 31 | * Get the barcode. 32 | */ 33 | public function getBarcode(): string 34 | { 35 | return $this->barcode; 36 | } 37 | 38 | /** 39 | * Get the width. 40 | */ 41 | public function getWidth(): int 42 | { 43 | return $this->width; 44 | } 45 | 46 | /** 47 | * Get the height. 48 | */ 49 | public function getHeight(): int 50 | { 51 | return $this->height; 52 | } 53 | 54 | /** 55 | * Get the bars. 56 | */ 57 | public function getBars(): array 58 | { 59 | return $this->bars; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/BarcodeBar.php: -------------------------------------------------------------------------------- 1 | width = $width; 22 | $this->height = $height; 23 | $this->positionVertical = $positionVertical; 24 | $this->type = $drawBar ? self::TYPE_BAR : self::TYPE_SPACING; 25 | } 26 | 27 | /** 28 | * Get the width. 29 | */ 30 | public function getWidth(): int 31 | { 32 | return $this->width; 33 | } 34 | 35 | /** 36 | * Get the height. 37 | */ 38 | public function getHeight(): int 39 | { 40 | return $this->height; 41 | } 42 | 43 | /** 44 | * Get position vertical. 45 | */ 46 | public function getPositionVertical(): int 47 | { 48 | return $this->positionVertical; 49 | } 50 | 51 | /** 52 | * Bar or not. 53 | */ 54 | public function isBar(): bool 55 | { 56 | return $this->type === self::TYPE_BAR; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/BarcodeManager.php: -------------------------------------------------------------------------------- 1 | container = $container; 53 | $this->config = $container->make('config'); 54 | } 55 | 56 | /** 57 | * Get a image type instance. 58 | */ 59 | public function imageType(?string $name = null): ImageType 60 | { 61 | $name = $name ?: $this->getDefaultDriver(); 62 | 63 | // If the given driver has not been created before, we will create the instances 64 | // here and cache it, so we can return it next time very quickly. If there is 65 | // already a driver created by this name, we'll just return that instance. 66 | if (! isset($this->imageTypes[$name])) { 67 | $this->imageTypes[$name] = $this->createDriver($name); 68 | } 69 | 70 | return $this->imageTypes[$name]; 71 | } 72 | 73 | /** 74 | * Create a new driver instance. 75 | * 76 | * @throws InvalidArgumentException 77 | */ 78 | protected function createDriver(string $driver): ImageType 79 | { 80 | // First, we will determine if a custom driver creator exists for the given driver and 81 | // if it does not we will check for a creator method for the driver. Custom creator 82 | // callbacks allow developers to build their own "drivers" easily using Closures. 83 | if (isset($this->customCreators[$driver])) { 84 | return $this->callCustomCreator($driver); 85 | } 86 | 87 | $method = 'create'.Str::studly($driver).'Driver'; 88 | 89 | if (method_exists($this, $method)) { 90 | return $this->$method(); 91 | } 92 | 93 | throw new InvalidArgumentException("ImageType [$driver] not supported."); 94 | } 95 | 96 | /** 97 | * Call a custom driver creator. 98 | */ 99 | protected function callCustomCreator(string $driver): ImageType 100 | { 101 | return $this->customCreators[$driver]($this->container); 102 | } 103 | 104 | /** 105 | * Create an HTML instance. 106 | */ 107 | public function createHtmlDriver(): ImageType 108 | { 109 | return new HTML; 110 | } 111 | 112 | /** 113 | * Create an DynamicHTML instance. 114 | */ 115 | public function createDynamicHtmlDriver(): ImageType 116 | { 117 | return new DynamicHTML; 118 | } 119 | 120 | /** 121 | * Create an JPG instance. 122 | */ 123 | public function createJpgDriver(): ImageType 124 | { 125 | return new JPG; 126 | } 127 | 128 | /** 129 | * Create an PNG instance. 130 | */ 131 | public function createPngDriver(): ImageType 132 | { 133 | return new PNG; 134 | } 135 | 136 | /** 137 | * Create an SVG instance. 138 | */ 139 | public function createSvgDriver(): ImageType 140 | { 141 | return new SVG; 142 | } 143 | 144 | /** 145 | * Generate Barcode. 146 | */ 147 | public function generate(string $text): string 148 | { 149 | return $this->imageType()->generate($text); 150 | } 151 | 152 | /** 153 | * Set the barcode type. 154 | */ 155 | public function type(BarcodeType|Type $type): ImageType 156 | { 157 | return $this->imageType()->type($type); 158 | } 159 | 160 | /** 161 | * Set the barcode foreground color. 162 | */ 163 | public function foregroundColor(string $foregroundColor): ImageType 164 | { 165 | return $this->imageType()->foregroundColor($foregroundColor); 166 | } 167 | 168 | /** 169 | * Set the barcode height. 170 | */ 171 | public function height(int $height): ImageType 172 | { 173 | return $this->imageType()->height($height); 174 | } 175 | 176 | /** 177 | * Set the barcode width factor. 178 | */ 179 | public function widthFactor(int $widthFactor): ImageType 180 | { 181 | return $this->imageType()->widthFactor($widthFactor); 182 | } 183 | 184 | /** 185 | * Force the use of Imagick image extension 186 | * 187 | * @throws BarcodeException 188 | */ 189 | public function useImagick(): ImageType 190 | { 191 | if (method_exists($this->imageType(), 'useImagick')) { 192 | if (! extension_loaded('imagick')) { 193 | throw new BarcodeException('The imagick is not installed!'); 194 | } 195 | 196 | return $this->imageType()->useImagick(); 197 | } 198 | 199 | throw new BarcodeException('This image type does not support useImagick function.'); 200 | } 201 | 202 | /** 203 | * Force the use of the GD image library 204 | * 205 | * @throws BarcodeException 206 | */ 207 | public function useGd(): ImageType 208 | { 209 | if (method_exists($this->imageType(), 'useGd')) { 210 | if (! function_exists('imagecreate')) { 211 | throw new BarcodeException('The GD is not installed!'); 212 | } 213 | 214 | return $this->imageType()->useGd(); 215 | } 216 | 217 | throw new BarcodeException('This image type does not support useGd function.'); 218 | } 219 | 220 | /** 221 | * Get the default driver name. 222 | */ 223 | public function getDefaultDriver(): string 224 | { 225 | return $this->config->get('barcode.image_type', 'png'); 226 | } 227 | 228 | /** 229 | * Register a custom driver creator Closure. 230 | */ 231 | public function extend(string $imageType, Closure $callback): self 232 | { 233 | $this->customCreators[$imageType] = $callback; 234 | 235 | return $this; 236 | } 237 | 238 | /** 239 | * Get the container instance used by the manager. 240 | */ 241 | public function getContainer(): Container 242 | { 243 | return $this->container; 244 | } 245 | 246 | /** 247 | * Set the container instance used by the manager. 248 | */ 249 | public function setContainer(Container $container): self 250 | { 251 | $this->container = $container; 252 | 253 | return $this; 254 | } 255 | 256 | /** 257 | * Unset the given disk instances. 258 | */ 259 | public function forgetImageType(array|string $imageType): self 260 | { 261 | foreach ((array) $imageType as $imageTypeName) { 262 | unset($this->imageTypes[$imageTypeName]); 263 | } 264 | 265 | return $this; 266 | } 267 | 268 | /** 269 | * Dynamically call the default driver instance. 270 | * 271 | * @return mixed 272 | */ 273 | public function __call(string $method, array $parameters) 274 | { 275 | return $this->imageType()->$method(...$parameters); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/BarcodeServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__.'/../config/barcode.php', 'barcode'); 15 | 16 | $this->app->bind('laravel-barcode', function ($app) { 17 | return new BarcodeManager($app); 18 | }); 19 | } 20 | 21 | public function boot(): void 22 | { 23 | $this->publishes([ 24 | __DIR__.'/../config/barcode.php' => config_path('barcode.php'), 25 | ], 'laravel-barcode-config'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Contracts/Factory.php: -------------------------------------------------------------------------------- 1 | getBarcodeData($text, $this->type); 21 | 22 | $html = '
'.PHP_EOL; 23 | 24 | $positionHorizontal = 0; 25 | /** @var BarcodeBar $bar */ 26 | foreach ($barcodeData->getBars() as $bar) { 27 | $barWidth = $bar->getWidth() / $barcodeData->getWidth() * 100; 28 | $barHeight = round(($bar->getHeight() / $barcodeData->getHeight() * 100), 3); 29 | 30 | if ($barWidth > 0 && $bar->isBar()) { 31 | $positionVertical = round(($bar->getPositionVertical() / $barcodeData->getHeight() * 100), 3); 32 | 33 | // draw a vertical bar 34 | $html .= '
 
'.PHP_EOL; 35 | } 36 | 37 | $positionHorizontal += $barWidth; 38 | } 39 | 40 | $html .= '
'.PHP_EOL; 41 | 42 | return $html; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Drivers/HTML.php: -------------------------------------------------------------------------------- 1 | getBarcodeData($text, $this->type); 17 | 18 | $html = '
'.PHP_EOL; 19 | 20 | $positionHorizontal = 0; 21 | /** @var BarcodeBar $bar */ 22 | foreach ($barcodeData->getBars() as $bar) { 23 | $barWidth = round(($bar->getWidth() * $this->widthFactor), 3); 24 | $barHeight = round(($bar->getHeight() * $this->height / $barcodeData->getHeight()), 3); 25 | 26 | if ($barWidth > 0 && $bar->isBar()) { 27 | $positionVertical = round(($bar->getPositionVertical() * $this->height / $barcodeData->getHeight()), 3); 28 | 29 | $html .= '
 
'.PHP_EOL; 30 | } 31 | 32 | $positionHorizontal += $barWidth; 33 | } 34 | 35 | $html .= '
'.PHP_EOL; 36 | 37 | return $html; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Drivers/JPG.php: -------------------------------------------------------------------------------- 1 | newImage($width, $height, 'white', 'JPG'); 13 | 14 | return $image; 15 | } 16 | 17 | protected function generateGdImage($image): void 18 | { 19 | imagejpeg($image); 20 | imagedestroy($image); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Drivers/PNG.php: -------------------------------------------------------------------------------- 1 | useImagick = true; 27 | } elseif (function_exists('imagecreate')) { 28 | $this->useImagick = false; 29 | } else { 30 | throw new BarcodeException('Neither gd-lib or imagick are installed!'); 31 | } 32 | } 33 | 34 | /** 35 | * Force the use of Imagick image extension. 36 | */ 37 | public function useImagick(): self 38 | { 39 | $this->useImagick = true; 40 | 41 | return $this; 42 | } 43 | 44 | /** 45 | * Force the use of the GD image library. 46 | */ 47 | public function useGd(): self 48 | { 49 | $this->useImagick = false; 50 | 51 | return $this; 52 | } 53 | 54 | /** 55 | * @param string $text code to print 56 | * 57 | * @throws \ImagickDrawException 58 | * @throws \ImagickException 59 | * @throws \ImagickPixelException 60 | */ 61 | public function generate(string $text): string 62 | { 63 | $barcodeData = $this->getBarcodeData($text, $this->type); 64 | $width = round($barcodeData->getWidth() * $this->widthFactor); 65 | 66 | $foregroundColor = $this->getForegroundColor(); 67 | 68 | if ($this->useImagick) { 69 | $imagickBarsShape = new imagickdraw; 70 | $imagickBarsShape->setFillColor(new imagickpixel('rgb('.implode(',', $foregroundColor).')')); 71 | } else { 72 | $image = $this->createGdImageObject($width, $this->height); 73 | $gdForegroundColor = imagecolorallocate($image, $foregroundColor[0], $foregroundColor[1], $foregroundColor[2]); 74 | } 75 | 76 | // print bars 77 | $positionHorizontal = 0; 78 | /** @var BarcodeBar $bar */ 79 | foreach ($barcodeData->getBars() as $bar) { 80 | $barWidth = round(($bar->getWidth() * $this->widthFactor), 3); 81 | 82 | if ($barWidth > 0 && $bar->isBar()) { 83 | $y = round(($bar->getPositionVertical() * $this->height / $barcodeData->getHeight()), 3); 84 | $barHeight = round(($bar->getHeight() * $this->height / $barcodeData->getHeight()), 3); 85 | 86 | // draw a vertical bar 87 | if ($this->useImagick) { 88 | $imagickBarsShape->rectangle($positionHorizontal, $y, ($positionHorizontal + $barWidth - 1), ($y + $barHeight)); 89 | } else { 90 | imagefilledrectangle($image, $positionHorizontal, $y, ($positionHorizontal + $barWidth - 1), ($y + $barHeight), $gdForegroundColor); 91 | } 92 | } 93 | $positionHorizontal += $barWidth; 94 | } 95 | 96 | if ($this->useImagick) { 97 | $image = $this->createImagickImageObject($width, $this->height); 98 | $image->drawImage($imagickBarsShape); 99 | 100 | return $image->getImageBlob(); 101 | } 102 | 103 | ob_start(); 104 | $this->generateGdImage($image); 105 | 106 | return ob_get_clean(); 107 | } 108 | 109 | public function getForegroundColor(): string|array 110 | { 111 | $color = Hex::fromString($this->foregroundColor)->toRgba(); 112 | 113 | return [$color->red(), $color->blue(), $color->green()]; 114 | } 115 | 116 | protected function createGdImageObject(int $width, int $height) 117 | { 118 | $image = imagecreate($width, $height); 119 | $colorBackground = imagecolorallocate($image, 255, 255, 255); 120 | imagecolortransparent($image, $colorBackground); 121 | 122 | return $image; 123 | } 124 | 125 | /** 126 | * @throws \ImagickException 127 | */ 128 | protected function createImagickImageObject(int $width, int $height): Imagick 129 | { 130 | $image = new Imagick; 131 | $image->newImage($width, $height, 'none', 'PNG'); 132 | 133 | return $image; 134 | } 135 | 136 | protected function generateGdImage($image): void 137 | { 138 | imagepng($image); 139 | imagedestroy($image); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Drivers/SVG.php: -------------------------------------------------------------------------------- 1 | getBarcodeData($text, $this->type); 18 | 19 | // replace table for special characters 20 | $repstr = ["\0" => '', '&' => '&', '<' => '<', '>' => '>']; 21 | 22 | $width = round(($barcodeData->getWidth() * $this->widthFactor), 3); 23 | 24 | $svg = ''.PHP_EOL; 25 | $svg .= ''.PHP_EOL; 26 | $svg .= ''.PHP_EOL; 27 | $svg .= "\t".''.strtr($barcodeData->getBarcode(), $repstr).''.PHP_EOL; 28 | $svg .= "\t".''.PHP_EOL; 29 | 30 | // print bars 31 | $positionHorizontal = 0; 32 | /** @var BarcodeBar $bar */ 33 | foreach ($barcodeData->getBars() as $bar) { 34 | $barWidth = round(($bar->getWidth() * $this->widthFactor), 3); 35 | $barHeight = round(($bar->getHeight() * $this->height / $barcodeData->getHeight()), 3); 36 | 37 | if ($bar->isBar() && $barWidth > 0) { 38 | $positionVertical = round(($bar->getPositionVertical() * $this->height / $barcodeData->getHeight()), 3); 39 | // draw a vertical bar 40 | $svg .= "\t\t".''.PHP_EOL; 41 | } 42 | 43 | $positionHorizontal += $barWidth; 44 | } 45 | $svg .= "\t".PHP_EOL; 46 | $svg .= ''.PHP_EOL; 47 | 48 | return $svg; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Enums/BarcodeType.php: -------------------------------------------------------------------------------- 1 | new TypeCode32, 106 | 107 | self::CODE_39 => new TypeCode39, 108 | 109 | self::CODE_39_CHECKSUM => new TypeCode39Checksum, 110 | 111 | self::CODE_39E => new TypeCode39Extended, 112 | 113 | self::CODE_39E_CHECKSUM => new TypeCode39ExtendedChecksum, 114 | 115 | self::CODE_93 => new TypeCode93, 116 | 117 | self::STANDARD_2_5 => new TypeStandard2of5, 118 | 119 | self::STANDARD_2_5_CHECKSUM => new TypeStandard2of5Checksum, 120 | 121 | self::INTERLEAVED_2_5 => new TypeInterleaved25, 122 | 123 | self::INTERLEAVED_2_5_CHECKSUM => new TypeInterleaved25Checksum, 124 | 125 | self::CODE_128 => new TypeCode128, 126 | 127 | self::CODE_128_A => new TypeCode128A, 128 | 129 | self::CODE_128_B => new TypeCode128B, 130 | 131 | self::CODE_128_C => new TypeCode128C, 132 | 133 | self::EAN_2 => new TypeUpcExtension2, 134 | 135 | self::EAN_5 => new TypeUpcExtension5, 136 | 137 | self::EAN_8 => new TypeEan8, 138 | 139 | self::EAN_13 => new TypeEan13, 140 | 141 | self::UPC_A => new TypeUpcA, 142 | 143 | self::UPC_E => new TypeUpcE, 144 | 145 | self::MSI => new TypeMsi, 146 | 147 | self::MSI_CHECKSUM => new TypeMsiChecksum, 148 | 149 | self::POSTNET => new TypePostnet, 150 | 151 | self::PLANET => new TypePlanet, 152 | 153 | self::RMS4CC => new TypeRms4cc, 154 | 155 | self::KIX => new TypeKix, 156 | 157 | self::IMB => new TypeIntelligentMailBarcode, 158 | 159 | self::CODABAR => new TypeCodabar, 160 | 161 | self::CODE_11 => new TypeCode11, 162 | 163 | self::PHARMA_CODE => new TypePharmacode, 164 | 165 | self::PHARMA_CODE_TWO_TRACKS => new TypePharmacodeTwoCode, 166 | }; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/Enums/Type.php: -------------------------------------------------------------------------------- 1 | new TypeCode32, 109 | 110 | self::TYPE_CODE_39 => new TypeCode39, 111 | 112 | self::TYPE_CODE_39_CHECKSUM => new TypeCode39Checksum, 113 | 114 | self::TYPE_CODE_39E => new TypeCode39Extended, 115 | 116 | self::TYPE_CODE_39E_CHECKSUM => new TypeCode39ExtendedChecksum, 117 | 118 | self::TYPE_CODE_93 => new TypeCode93, 119 | 120 | self::TYPE_STANDARD_2_5 => new TypeStandard2of5, 121 | 122 | self::TYPE_STANDARD_2_5_CHECKSUM => new TypeStandard2of5Checksum, 123 | 124 | self::TYPE_INTERLEAVED_2_5 => new TypeInterleaved25, 125 | 126 | self::TYPE_INTERLEAVED_2_5_CHECKSUM => new TypeInterleaved25Checksum, 127 | 128 | self::TYPE_CODE_128 => new TypeCode128, 129 | 130 | self::TYPE_CODE_128_A => new TypeCode128A, 131 | 132 | self::TYPE_CODE_128_B => new TypeCode128B, 133 | 134 | self::TYPE_CODE_128_C => new TypeCode128C, 135 | 136 | self::TYPE_EAN_2 => new TypeUpcExtension2, 137 | 138 | self::TYPE_EAN_5 => new TypeUpcExtension5, 139 | 140 | self::TYPE_EAN_8 => new TypeEan8, 141 | 142 | self::TYPE_EAN_13 => new TypeEan13, 143 | 144 | self::TYPE_UPC_A => new TypeUpcA, 145 | 146 | self::TYPE_UPC_E => new TypeUpcE, 147 | 148 | self::TYPE_MSI => new TypeMsi, 149 | 150 | self::TYPE_MSI_CHECKSUM => new TypeMsiChecksum, 151 | 152 | self::TYPE_POSTNET => new TypePostnet, 153 | 154 | self::TYPE_PLANET => new TypePlanet, 155 | 156 | self::TYPE_RMS4CC => new TypeRms4cc, 157 | 158 | self::TYPE_KIX => new TypeKix, 159 | 160 | self::TYPE_IMB => new TypeIntelligentMailBarcode, 161 | 162 | self::TYPE_CODABAR => new TypeCodabar, 163 | 164 | self::TYPE_CODE_11 => new TypeCode11, 165 | 166 | self::TYPE_PHARMA_CODE => new TypePharmacode, 167 | 168 | self::TYPE_PHARMA_CODE_TWO_TRACKS => new TypePharmacodeTwoCode, 169 | }; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/Exceptions/BarcodeException.php: -------------------------------------------------------------------------------- 1 | 0) { 20 | $r = (10 - $r); 21 | } 22 | 23 | return $r; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Helpers/BinarySequenceConverter.php: -------------------------------------------------------------------------------- 1 | addBar(new BarcodeBar($barWidth, 1, $drawBar)); 34 | $barWidth = 0; 35 | } 36 | } 37 | 38 | return $barcode; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Types/TypeCodabar.php: -------------------------------------------------------------------------------- 1 | '11111221', 18 | '1' => '11112211', 19 | '2' => '11121121', 20 | '3' => '22111111', 21 | '4' => '11211211', 22 | '5' => '21111211', 23 | '6' => '12111121', 24 | '7' => '12112111', 25 | '8' => '12211111', 26 | '9' => '21121111', 27 | '-' => '11122111', 28 | '$' => '11221111', 29 | ':' => '21112121', 30 | '/' => '21211121', 31 | '.' => '21212111', 32 | '+' => '11222221', 33 | 'A' => '11221211', 34 | 'B' => '12121121', 35 | 'C' => '11121221', 36 | 'D' => '11122211', 37 | ]; 38 | 39 | /** 40 | * @throws InvalidCharacterException 41 | */ 42 | public function getBarcodeData(string $code): Barcode 43 | { 44 | $barcode = new Barcode($code); 45 | 46 | $code = 'A'.strtoupper($code).'A'; 47 | 48 | for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) { 49 | if (! isset($this->conversionTable[$code[$i]])) { 50 | throw new InvalidCharacterException('Char '.$code[$i].' is unsupported'); 51 | } 52 | 53 | $seq = $this->conversionTable[$code[$i]]; 54 | for ($j = 0; $j < 8; $j++) { 55 | if (($j % 2) === 0) { 56 | $drawBar = true; 57 | } else { 58 | $drawBar = false; 59 | } 60 | $barWidth = $seq[$j]; 61 | $barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar)); 62 | } 63 | } 64 | 65 | return $barcode; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Types/TypeCode11.php: -------------------------------------------------------------------------------- 1 | '111121', 18 | '1' => '211121', 19 | '2' => '121121', 20 | '3' => '221111', 21 | '4' => '112121', 22 | '5' => '212111', 23 | '6' => '122111', 24 | '7' => '111221', 25 | '8' => '211211', 26 | '9' => '211111', 27 | '-' => '112111', 28 | 'S' => '112211', 29 | ]; 30 | 31 | /** 32 | * @throws InvalidCharacterException 33 | */ 34 | public function getBarcodeData(string $code): Barcode 35 | { 36 | $barcode = new Barcode($code); 37 | 38 | $code .= $this->getCheckDigitC($code); 39 | $code .= $this->getCheckDigitK($code); 40 | 41 | $code = 'S'.$code.'S'; 42 | 43 | for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) { 44 | if (! isset($this->conversionTable[$code[$i]])) { 45 | throw new InvalidCharacterException('Char '.$code[$i].' is unsupported'); 46 | } 47 | 48 | $seq = $this->conversionTable[$code[$i]]; 49 | for ($j = 0, $jMax = strlen($seq); $j < $jMax; $j++) { 50 | if (($j % 2) === 0) { 51 | $drawBar = true; 52 | } else { 53 | $drawBar = false; 54 | } 55 | $barWidth = $seq[$j]; 56 | 57 | $barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar)); 58 | } 59 | } 60 | 61 | return $barcode; 62 | } 63 | 64 | private function getCheckDigitC(string $code): string 65 | { 66 | $p = 1; 67 | $check = 0; 68 | for ($i = (strlen($code) - 1); $i >= 0; $i--) { 69 | $digit = $code[$i]; 70 | if ($digit === '-') { 71 | $dval = 10; 72 | } else { 73 | $dval = (int) $digit; 74 | } 75 | $check += ($dval * $p); 76 | $p++; 77 | if ($p > 10) { 78 | $p = 1; 79 | } 80 | } 81 | $check %= 11; 82 | if ($check === 10) { 83 | $check = '-'; 84 | } 85 | 86 | return $check; 87 | } 88 | 89 | private function getCheckDigitK(string $code): string 90 | { 91 | if (strlen($code) <= 10) { 92 | return ''; 93 | } 94 | 95 | $p = 1; 96 | $check = 0; 97 | for ($i = (strlen($code) - 1); $i >= 0; $i--) { 98 | $digit = $code[$i]; 99 | if ($digit === '-') { 100 | $dval = 10; 101 | } else { 102 | $dval = (int) $digit; 103 | } 104 | $check += ($dval * $p); 105 | $p++; 106 | if ($p > 9) { 107 | $p = 1; 108 | } 109 | } 110 | $check %= 11; 111 | 112 | return (string) $check; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Types/TypeCode128.php: -------------------------------------------------------------------------------- 1 | ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'; 145 | $keys_a .= chr(0).chr(1).chr(2).chr(3).chr(4).chr(5).chr(6).chr(7).chr(8).chr(9); 146 | $keys_a .= chr(10).chr(11).chr(12).chr(13).chr(14).chr(15).chr(16).chr(17).chr(18).chr(19); 147 | $keys_a .= chr(20).chr(21).chr(22).chr(23).chr(24).chr(25).chr(26).chr(27).chr(28).chr(29); 148 | $keys_a .= chr(30).chr(31); 149 | 150 | // ASCII characters for code B (ASCII 32 - 127) 151 | $keys_b = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.chr(127); 152 | 153 | // special codes 154 | $fnc_a = [241 => 102, 242 => 97, 243 => 96, 244 => 101]; 155 | $fnc_b = [241 => 102, 242 => 97, 243 => 96, 244 => 100]; 156 | 157 | // array of symbols 158 | $code_data = []; 159 | 160 | // length of the code 161 | $len = strlen($code); 162 | 163 | switch (strtoupper($this->type ?? '')) { 164 | case 'A': 165 | $startid = 103; 166 | for ($i = 0; $i < $len; $i++) { 167 | $char = $code[$i]; 168 | $char_id = ord($char); 169 | if (($char_id <= 244) && ($char_id >= 241)) { 170 | $code_data[] = $fnc_a[$char_id]; 171 | } elseif ($char_id <= 95) { 172 | $code_data[] = strpos($keys_a, $char); 173 | } else { 174 | throw new InvalidCharacterException('Char '.$char.' is unsupported'); 175 | } 176 | } 177 | break; 178 | 179 | case 'B': 180 | $startid = 104; 181 | for ($i = 0; $i < $len; $i++) { 182 | $char = $code[$i]; 183 | $char_id = ord($char); 184 | if (($char_id >= 241) && ($char_id <= 244)) { 185 | $code_data[] = $fnc_b[$char_id]; 186 | } elseif (($char_id >= 32) && ($char_id <= 127)) { 187 | $code_data[] = strpos($keys_b, $char); 188 | } else { 189 | throw new InvalidCharacterException('Char '.$char.' is unsupported'); 190 | } 191 | } 192 | break; 193 | 194 | case 'C': 195 | $startid = 105; 196 | if (ord($code[0]) === 241) { 197 | $code_data[] = 102; 198 | $code = substr($code, 1); 199 | $len--; 200 | } 201 | if (($len % 2) !== 0) { 202 | throw new InvalidLengthException('Length must be even'); 203 | } 204 | for ($i = 0; $i < $len; $i += 2) { 205 | $chrnum = $code[$i].$code[$i + 1]; 206 | if (preg_match('/(\d{2})/', $chrnum) > 0) { 207 | $code_data[] = (int) $chrnum; 208 | } else { 209 | throw new InvalidCharacterException; 210 | } 211 | } 212 | break; 213 | 214 | default: 215 | // split code into sequences 216 | $sequence = []; 217 | // get numeric sequences (if any) 218 | $numseq = []; 219 | preg_match_all('/(\d{4,})/', $code, $numseq, PREG_OFFSET_CAPTURE); 220 | 221 | if (! empty($numseq[1])) { 222 | $end_offset = 0; 223 | 224 | foreach ($numseq[1] as $val) { 225 | $offset = $val[1]; 226 | 227 | // numeric sequence 228 | $slen = strlen($val[0]); 229 | if (($slen % 2) != 0) { 230 | // the length must be even 231 | $offset++; 232 | $val[0] = substr($val[0], 1); 233 | } 234 | 235 | if ($offset > $end_offset) { 236 | // non numeric sequence 237 | $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset, ($offset - $end_offset)))); 238 | } 239 | 240 | // numeric sequence fallback 241 | $slen = strlen($val[0]); 242 | if (($slen % 2) != 0) { 243 | // the length must be even 244 | $slen--; 245 | } 246 | $sequence[] = ['C', substr($code, $offset, $slen), $slen]; 247 | $end_offset = $offset + $slen; 248 | } 249 | if ($end_offset < $len) { 250 | $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset))); 251 | } 252 | } else { 253 | // text code (non C mode) 254 | $sequence = array_merge($sequence, $this->get128ABsequence($code)); 255 | } 256 | 257 | $startid = 0; 258 | // process the sequence 259 | foreach ($sequence as $key => $seq) { 260 | switch ($seq[0]) { 261 | case 'A': 262 | if ($key === 0) { 263 | $startid = 103; 264 | } elseif ($sequence[($key - 1)][0] !== 'A') { 265 | if (($seq[2] === 1) && ($key > 0) && ($sequence[($key - 1)][0] === 'B') && (! isset($sequence[($key - 1)][3]))) { 266 | // single character shift 267 | $code_data[] = 98; 268 | // mark shift 269 | $sequence[$key][3] = true; 270 | } elseif (! isset($sequence[($key - 1)][3])) { 271 | $code_data[] = 101; 272 | } 273 | } 274 | for ($i = 0; $i < $seq[2]; $i++) { 275 | $char = $seq[1][$i]; 276 | $char_id = ord($char); 277 | if (($char_id >= 241) && ($char_id <= 244)) { 278 | $code_data[] = $fnc_a[$char_id]; 279 | } else { 280 | $code_data[] = strpos($keys_a, $char); 281 | } 282 | } 283 | break; 284 | 285 | case 'B': 286 | if ($key === 0) { 287 | $tmpchr = ord($seq[1][0]); 288 | if (($seq[2] === 1) && ($tmpchr >= 241) && ($tmpchr <= 244) && isset($sequence[($key + 1)]) && ($sequence[($key + 1)][0] !== 'B')) { 289 | switch ($sequence[($key + 1)][0]) { 290 | case 'A': 291 | 292 | $startid = 103; 293 | $sequence[$key][0] = 'A'; 294 | $code_data[] = $fnc_a[$tmpchr]; 295 | break; 296 | 297 | case 'C': 298 | 299 | $startid = 105; 300 | $sequence[$key][0] = 'C'; 301 | $code_data[] = $fnc_a[$tmpchr]; 302 | break; 303 | } 304 | break; 305 | } 306 | 307 | $startid = 104; 308 | } elseif ($sequence[($key - 1)][0] !== 'B') { 309 | if (($seq[2] === 1) && ($key > 0) && ($sequence[($key - 1)][0] === 'A') && (! isset($sequence[($key - 1)][3]))) { 310 | // single character shift 311 | $code_data[] = 98; 312 | // mark shift 313 | $sequence[$key][3] = true; 314 | } elseif (! isset($sequence[($key - 1)][3])) { 315 | $code_data[] = 100; 316 | } 317 | } 318 | for ($i = 0; $i < $seq[2]; $i++) { 319 | $char = $seq[1][$i]; 320 | $char_id = ord($char); 321 | if (($char_id >= 241) && ($char_id <= 244)) { 322 | $code_data[] = $fnc_b[$char_id]; 323 | } else { 324 | $code_data[] = strpos($keys_b, $char); 325 | } 326 | } 327 | break; 328 | 329 | case 'C': 330 | if ($key === 0) { 331 | $startid = 105; 332 | } elseif ($sequence[($key - 1)][0] !== 'C') { 333 | $code_data[] = 99; 334 | } 335 | for ($i = 0; $i < $seq[2]; $i += 2) { 336 | $chrnum = $seq[1][$i].$seq[1][$i + 1]; 337 | $code_data[] = (int) $chrnum; 338 | } 339 | break; 340 | } 341 | } 342 | } 343 | 344 | // calculate check character 345 | $sum = $startid; 346 | foreach ($code_data as $key => $val) { 347 | $sum += ($val * ($key + 1)); 348 | } 349 | // add check character 350 | $code_data[] = ($sum % 103); 351 | // add stop sequence 352 | $code_data[] = 106; 353 | $code_data[] = 107; 354 | // add start code at the beginning 355 | array_unshift($code_data, $startid); 356 | 357 | // build barcode array 358 | $barcode = new Barcode($code); 359 | foreach ($code_data as $val) { 360 | $seq = $this->conversionTable[$val]; 361 | for ($j = 0; $j < 6; $j++) { 362 | if (($j % 2) === 0) { 363 | $t = true; // bar 364 | } else { 365 | $t = false; // space 366 | } 367 | $w = $seq[$j]; 368 | 369 | $barcode->addBar(new BarcodeBar($w, 1, $t)); 370 | } 371 | } 372 | 373 | return $barcode; 374 | } 375 | 376 | /** 377 | * Split text code in A/B sequence for 128 code 378 | * 379 | * @param $code (string) code to split. 380 | */ 381 | protected function get128ABsequence(string $code): array 382 | { 383 | $len = strlen($code); 384 | $sequence = []; 385 | // get A sequences (if any) 386 | $numseq = []; 387 | preg_match_all('/([\x00-\x1f])/', $code, $numseq, PREG_OFFSET_CAPTURE); 388 | if (! empty($numseq[1])) { 389 | $end_offset = 0; 390 | foreach ($numseq[1] as $val) { 391 | $offset = $val[1]; 392 | if ($offset > $end_offset) { 393 | // B sequence 394 | $sequence[] = [ 395 | 'B', 396 | substr($code, $end_offset, ($offset - $end_offset)), 397 | ($offset - $end_offset), 398 | ]; 399 | } 400 | // A sequence 401 | $slen = strlen($val[0]); 402 | $sequence[] = ['A', substr($code, $offset, $slen), $slen]; 403 | $end_offset = $offset + $slen; 404 | } 405 | if ($end_offset < $len) { 406 | $sequence[] = ['B', substr($code, $end_offset), ($len - $end_offset)]; 407 | } 408 | } else { 409 | // only B sequence 410 | $sequence[] = ['B', $code, $len]; 411 | } 412 | 413 | return $sequence; 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/Types/TypeCode128A.php: -------------------------------------------------------------------------------- 1 | '0', 15 | '1' => '1', 16 | '2' => '2', 17 | '3' => '3', 18 | '4' => '4', 19 | '5' => '5', 20 | '6' => '6', 21 | '7' => '7', 22 | '8' => '8', 23 | '9' => '9', 24 | '10' => 'B', 25 | '11' => 'C', 26 | '12' => 'D', 27 | '13' => 'F', 28 | '14' => 'G', 29 | '15' => 'H', 30 | '16' => 'J', 31 | '17' => 'K', 32 | '18' => 'L', 33 | '19' => 'M', 34 | '20' => 'N', 35 | '21' => 'P', 36 | '22' => 'Q', 37 | '23' => 'R', 38 | '24' => 'S', 39 | '25' => 'T', 40 | '26' => 'U', 41 | '27' => 'V', 42 | '28' => 'W', 43 | '29' => 'X', 44 | '30' => 'Y', 45 | '31' => 'Z', 46 | ]; 47 | 48 | public function getBarcodeData(string $code): Barcode 49 | { 50 | $code39 = ''; 51 | $codeElab = $code; 52 | 53 | for ($e = 5; $e >= 0; $e--) { 54 | $code39 .= $this->conversionTable32[(int) ($codeElab / (32 ** $e))]; 55 | $codeElab %= 32 ** $e; 56 | } 57 | 58 | return parent::getBarcodeData($code39); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Types/TypeCode39.php: -------------------------------------------------------------------------------- 1 | '111331311', 23 | '1' => '311311113', 24 | '2' => '113311113', 25 | '3' => '313311111', 26 | '4' => '111331113', 27 | '5' => '311331111', 28 | '6' => '113331111', 29 | '7' => '111311313', 30 | '8' => '311311311', 31 | '9' => '113311311', 32 | 'A' => '311113113', 33 | 'B' => '113113113', 34 | 'C' => '313113111', 35 | 'D' => '111133113', 36 | 'E' => '311133111', 37 | 'F' => '113133111', 38 | 'G' => '111113313', 39 | 'H' => '311113311', 40 | 'I' => '113113311', 41 | 'J' => '111133311', 42 | 'K' => '311111133', 43 | 'L' => '113111133', 44 | 'M' => '313111131', 45 | 'N' => '111131133', 46 | 'O' => '311131131', 47 | 'P' => '113131131', 48 | 'Q' => '111111333', 49 | 'R' => '311111331', 50 | 'S' => '113111331', 51 | 'T' => '111131331', 52 | 'U' => '331111113', 53 | 'V' => '133111113', 54 | 'W' => '333111111', 55 | 'X' => '131131113', 56 | 'Y' => '331131111', 57 | 'Z' => '133131111', 58 | '-' => '131111313', 59 | '.' => '331111311', 60 | ' ' => '133111311', 61 | '$' => '131313111', 62 | '/' => '131311131', 63 | '+' => '131113131', 64 | '%' => '111313131', 65 | '*' => '131131311', 66 | ]; 67 | 68 | /** 69 | * @throws InvalidCharacterException 70 | * @throws InvalidLengthException 71 | */ 72 | public function getBarcodeData(string $code): Barcode 73 | { 74 | if (trim($code) === '') { 75 | throw new InvalidLengthException('You should provide a barcode string.'); 76 | } 77 | 78 | if ($this->extended) { 79 | // extended mode 80 | $code = $this->encode_code39_ext($code); 81 | } 82 | 83 | if ($this->checksum) { 84 | // checksum 85 | $code .= $this->checksum_code39($code); 86 | } 87 | 88 | // add start and stop codes 89 | $code = '*'.$code.'*'; 90 | 91 | $barcode = new Barcode($code); 92 | 93 | for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) { 94 | $char = $code[$i]; 95 | if (! isset($this->conversionTable[$char])) { 96 | throw new InvalidCharacterException('Char '.$char.' is unsupported'); 97 | } 98 | 99 | for ($j = 0; $j < 9; $j++) { 100 | if (($j % 2) === 0) { 101 | $drawBar = true; 102 | } else { 103 | $drawBar = false; 104 | } 105 | $barWidth = $this->conversionTable[$char][$j]; 106 | $barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar)); 107 | } 108 | 109 | // inter character gap 110 | $barcode->addBar(new BarcodeBar(1, 1, false)); 111 | } 112 | 113 | return $barcode; 114 | } 115 | 116 | /** 117 | * Encode a string to be used for CODE 39 Extended mode. 118 | * 119 | * @param string $code code to represent. 120 | * 121 | * @throws InvalidCharacterException 122 | */ 123 | protected function encode_code39_ext(string $code): string 124 | { 125 | $encode = [ 126 | chr(0) => '%U', 127 | chr(1) => '$A', 128 | chr(2) => '$B', 129 | chr(3) => '$C', 130 | chr(4) => '$D', 131 | chr(5) => '$E', 132 | chr(6) => '$F', 133 | chr(7) => '$G', 134 | chr(8) => '$H', 135 | chr(9) => '$I', 136 | chr(10) => '$J', 137 | chr(11) => '$K', 138 | chr(12) => '$L', 139 | chr(13) => '$M', 140 | chr(14) => '$N', 141 | chr(15) => '$O', 142 | chr(16) => '$P', 143 | chr(17) => '$Q', 144 | chr(18) => '$R', 145 | chr(19) => '$S', 146 | chr(20) => '$T', 147 | chr(21) => '$U', 148 | chr(22) => '$V', 149 | chr(23) => '$W', 150 | chr(24) => '$X', 151 | chr(25) => '$Y', 152 | chr(26) => '$Z', 153 | chr(27) => '%A', 154 | chr(28) => '%B', 155 | chr(29) => '%C', 156 | chr(30) => '%D', 157 | chr(31) => '%E', 158 | chr(32) => ' ', 159 | chr(33) => '/A', 160 | chr(34) => '/B', 161 | chr(35) => '/C', 162 | chr(36) => '/D', 163 | chr(37) => '/E', 164 | chr(38) => '/F', 165 | chr(39) => '/G', 166 | chr(40) => '/H', 167 | chr(41) => '/I', 168 | chr(42) => '/J', 169 | chr(43) => '/K', 170 | chr(44) => '/L', 171 | chr(45) => '-', 172 | chr(46) => '.', 173 | chr(47) => '/O', 174 | chr(48) => '0', 175 | chr(49) => '1', 176 | chr(50) => '2', 177 | chr(51) => '3', 178 | chr(52) => '4', 179 | chr(53) => '5', 180 | chr(54) => '6', 181 | chr(55) => '7', 182 | chr(56) => '8', 183 | chr(57) => '9', 184 | chr(58) => '/Z', 185 | chr(59) => '%F', 186 | chr(60) => '%G', 187 | chr(61) => '%H', 188 | chr(62) => '%I', 189 | chr(63) => '%J', 190 | chr(64) => '%V', 191 | chr(65) => 'A', 192 | chr(66) => 'B', 193 | chr(67) => 'C', 194 | chr(68) => 'D', 195 | chr(69) => 'E', 196 | chr(70) => 'F', 197 | chr(71) => 'G', 198 | chr(72) => 'H', 199 | chr(73) => 'I', 200 | chr(74) => 'J', 201 | chr(75) => 'K', 202 | chr(76) => 'L', 203 | chr(77) => 'M', 204 | chr(78) => 'N', 205 | chr(79) => 'O', 206 | chr(80) => 'P', 207 | chr(81) => 'Q', 208 | chr(82) => 'R', 209 | chr(83) => 'S', 210 | chr(84) => 'T', 211 | chr(85) => 'U', 212 | chr(86) => 'V', 213 | chr(87) => 'W', 214 | chr(88) => 'X', 215 | chr(89) => 'Y', 216 | chr(90) => 'Z', 217 | chr(91) => '%K', 218 | chr(92) => '%L', 219 | chr(93) => '%M', 220 | chr(94) => '%N', 221 | chr(95) => '%O', 222 | chr(96) => '%W', 223 | chr(97) => '+A', 224 | chr(98) => '+B', 225 | chr(99) => '+C', 226 | chr(100) => '+D', 227 | chr(101) => '+E', 228 | chr(102) => '+F', 229 | chr(103) => '+G', 230 | chr(104) => '+H', 231 | chr(105) => '+I', 232 | chr(106) => '+J', 233 | chr(107) => '+K', 234 | chr(108) => '+L', 235 | chr(109) => '+M', 236 | chr(110) => '+N', 237 | chr(111) => '+O', 238 | chr(112) => '+P', 239 | chr(113) => '+Q', 240 | chr(114) => '+R', 241 | chr(115) => '+S', 242 | chr(116) => '+T', 243 | chr(117) => '+U', 244 | chr(118) => '+V', 245 | chr(119) => '+W', 246 | chr(120) => '+X', 247 | chr(121) => '+Y', 248 | chr(122) => '+Z', 249 | chr(123) => '%P', 250 | chr(124) => '%Q', 251 | chr(125) => '%R', 252 | chr(126) => '%S', 253 | chr(127) => '%T', 254 | ]; 255 | 256 | $code_ext = ''; 257 | for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) { 258 | if (ord($code[$i]) > 127) { 259 | throw new InvalidCharacterException('Only supports till char 127'); 260 | } 261 | 262 | $code_ext .= $encode[$code[$i]]; 263 | } 264 | 265 | return $code_ext; 266 | } 267 | 268 | /** 269 | * Calculate CODE 39 checksum (modulo 43). 270 | * 271 | * @param string $code code to represent. 272 | */ 273 | protected function checksum_code39(string $code): string 274 | { 275 | $chars = [ 276 | '0', 277 | '1', 278 | '2', 279 | '3', 280 | '4', 281 | '5', 282 | '6', 283 | '7', 284 | '8', 285 | '9', 286 | 'A', 287 | 'B', 288 | 'C', 289 | 'D', 290 | 'E', 291 | 'F', 292 | 'G', 293 | 'H', 294 | 'I', 295 | 'J', 296 | 'K', 297 | 'L', 298 | 'M', 299 | 'N', 300 | 'O', 301 | 'P', 302 | 'Q', 303 | 'R', 304 | 'S', 305 | 'T', 306 | 'U', 307 | 'V', 308 | 'W', 309 | 'X', 310 | 'Y', 311 | 'Z', 312 | '-', 313 | '.', 314 | ' ', 315 | '$', 316 | '/', 317 | '+', 318 | '%', 319 | ]; 320 | 321 | $sum = 0; 322 | for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) { 323 | $k = array_keys($chars, $code[$i]); 324 | $sum += $k[0]; 325 | } 326 | $j = ($sum % 43); 327 | 328 | return $chars[$j]; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/Types/TypeCode39Checksum.php: -------------------------------------------------------------------------------- 1 | '131112', // 0 20 | 49 => '111213', // 1 21 | 50 => '111312', // 2 22 | 51 => '111411', // 3 23 | 52 => '121113', // 4 24 | 53 => '121212', // 5 25 | 54 => '121311', // 6 26 | 55 => '111114', // 7 27 | 56 => '131211', // 8 28 | 57 => '141111', // 9 29 | 65 => '211113', // A 30 | 66 => '211212', // B 31 | 67 => '211311', // C 32 | 68 => '221112', // D 33 | 69 => '221211', // E 34 | 70 => '231111', // F 35 | 71 => '112113', // G 36 | 72 => '112212', // H 37 | 73 => '112311', // I 38 | 74 => '122112', // J 39 | 75 => '132111', // K 40 | 76 => '111123', // L 41 | 77 => '111222', // M 42 | 78 => '111321', // N 43 | 79 => '121122', // O 44 | 80 => '131121', // P 45 | 81 => '212112', // Q 46 | 82 => '212211', // R 47 | 83 => '211122', // S 48 | 84 => '211221', // T 49 | 85 => '221121', // U 50 | 86 => '222111', // V 51 | 87 => '112122', // W 52 | 88 => '112221', // X 53 | 89 => '122121', // Y 54 | 90 => '123111', // Z 55 | 45 => '121131', // - 56 | 46 => '311112', // . 57 | 32 => '311211', // 58 | 36 => '321111', // $ 59 | 47 => '112131', // / 60 | 43 => '113121', // + 61 | 37 => '211131', // % 62 | 97 => '121221', // ($) 63 | 98 => '312111', // (%) 64 | 99 => '311121', // (/) 65 | 100 => '122211', // (+) 66 | 42 => '111141', // start-stop 67 | ]; 68 | 69 | /** 70 | * @throws InvalidCharacterException 71 | */ 72 | public function getBarcodeData(string $code): Barcode 73 | { 74 | $encode = [ 75 | chr(0) => 'bU', 76 | chr(1) => 'aA', 77 | chr(2) => 'aB', 78 | chr(3) => 'aC', 79 | chr(4) => 'aD', 80 | chr(5) => 'aE', 81 | chr(6) => 'aF', 82 | chr(7) => 'aG', 83 | chr(8) => 'aH', 84 | chr(9) => 'aI', 85 | chr(10) => 'aJ', 86 | chr(11) => 'aK', 87 | chr(12) => 'aL', 88 | chr(13) => 'aM', 89 | chr(14) => 'aN', 90 | chr(15) => 'aO', 91 | chr(16) => 'aP', 92 | chr(17) => 'aQ', 93 | chr(18) => 'aR', 94 | chr(19) => 'aS', 95 | chr(20) => 'aT', 96 | chr(21) => 'aU', 97 | chr(22) => 'aV', 98 | chr(23) => 'aW', 99 | chr(24) => 'aX', 100 | chr(25) => 'aY', 101 | chr(26) => 'aZ', 102 | chr(27) => 'bA', 103 | chr(28) => 'bB', 104 | chr(29) => 'bC', 105 | chr(30) => 'bD', 106 | chr(31) => 'bE', 107 | chr(32) => ' ', 108 | chr(33) => 'cA', 109 | chr(34) => 'cB', 110 | chr(35) => 'cC', 111 | chr(36) => '$', 112 | chr(37) => '%', 113 | chr(38) => 'cF', 114 | chr(39) => 'cG', 115 | chr(40) => 'cH', 116 | chr(41) => 'cI', 117 | chr(42) => 'cJ', 118 | chr(43) => '+', 119 | chr(44) => 'cL', 120 | chr(45) => '-', 121 | chr(46) => '.', 122 | chr(47) => '/', 123 | chr(48) => '0', 124 | chr(49) => '1', 125 | chr(50) => '2', 126 | chr(51) => '3', 127 | chr(52) => '4', 128 | chr(53) => '5', 129 | chr(54) => '6', 130 | chr(55) => '7', 131 | chr(56) => '8', 132 | chr(57) => '9', 133 | chr(58) => 'cZ', 134 | chr(59) => 'bF', 135 | chr(60) => 'bG', 136 | chr(61) => 'bH', 137 | chr(62) => 'bI', 138 | chr(63) => 'bJ', 139 | chr(64) => 'bV', 140 | chr(65) => 'A', 141 | chr(66) => 'B', 142 | chr(67) => 'C', 143 | chr(68) => 'D', 144 | chr(69) => 'E', 145 | chr(70) => 'F', 146 | chr(71) => 'G', 147 | chr(72) => 'H', 148 | chr(73) => 'I', 149 | chr(74) => 'J', 150 | chr(75) => 'K', 151 | chr(76) => 'L', 152 | chr(77) => 'M', 153 | chr(78) => 'N', 154 | chr(79) => 'O', 155 | chr(80) => 'P', 156 | chr(81) => 'Q', 157 | chr(82) => 'R', 158 | chr(83) => 'S', 159 | chr(84) => 'T', 160 | chr(85) => 'U', 161 | chr(86) => 'V', 162 | chr(87) => 'W', 163 | chr(88) => 'X', 164 | chr(89) => 'Y', 165 | chr(90) => 'Z', 166 | chr(91) => 'bK', 167 | chr(92) => 'bL', 168 | chr(93) => 'bM', 169 | chr(94) => 'bN', 170 | chr(95) => 'bO', 171 | chr(96) => 'bW', 172 | chr(97) => 'dA', 173 | chr(98) => 'dB', 174 | chr(99) => 'dC', 175 | chr(100) => 'dD', 176 | chr(101) => 'dE', 177 | chr(102) => 'dF', 178 | chr(103) => 'dG', 179 | chr(104) => 'dH', 180 | chr(105) => 'dI', 181 | chr(106) => 'dJ', 182 | chr(107) => 'dK', 183 | chr(108) => 'dL', 184 | chr(109) => 'dM', 185 | chr(110) => 'dN', 186 | chr(111) => 'dO', 187 | chr(112) => 'dP', 188 | chr(113) => 'dQ', 189 | chr(114) => 'dR', 190 | chr(115) => 'dS', 191 | chr(116) => 'dT', 192 | chr(117) => 'dU', 193 | chr(118) => 'dV', 194 | chr(119) => 'dW', 195 | chr(120) => 'dX', 196 | chr(121) => 'dY', 197 | chr(122) => 'dZ', 198 | chr(123) => 'bP', 199 | chr(124) => 'bQ', 200 | chr(125) => 'bR', 201 | chr(126) => 'bS', 202 | chr(127) => 'bT', 203 | ]; 204 | 205 | $code_ext = ''; 206 | $clen = strlen($code); 207 | for ($i = 0; $i < $clen; $i++) { 208 | if (ord($code[$i]) > 127) { 209 | throw new InvalidCharacterException('Only supports till char 127'); 210 | } 211 | $code_ext .= $encode[$code[$i]]; 212 | } 213 | 214 | // checksum 215 | $code_ext .= $this->checksum_code93($code_ext); 216 | 217 | // add start and stop codes 218 | $code = '*'.$code_ext.'*'; 219 | 220 | $barcode = new Barcode($code); 221 | 222 | for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) { 223 | $char = ord($code[$i]); 224 | if (! isset($this->conversionTable[$char])) { 225 | throw new InvalidCharacterException('Char '.$char.' is unsupported'); 226 | } 227 | 228 | for ($j = 0; $j < 6; $j++) { 229 | if (($j % 2) === 0) { 230 | $drawBar = true; 231 | } else { 232 | $drawBar = false; 233 | } 234 | $barWidth = $this->conversionTable[$char][$j]; 235 | 236 | $barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar)); 237 | } 238 | } 239 | 240 | $barcode->addBar(new BarcodeBar(1, 1, true)); 241 | 242 | return $barcode; 243 | } 244 | 245 | /** 246 | * Calculate CODE 93 checksum (modulo 47). 247 | * 248 | * @param $code (string) code to represent. 249 | */ 250 | protected function checksum_code93(string $code): string 251 | { 252 | $chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%', 'a', 'b', 'c', 'd']; 253 | 254 | // calculate check digit C 255 | $len = strlen($code); 256 | $p = 1; 257 | $check = 0; 258 | for ($i = ($len - 1); $i >= 0; $i--) { 259 | $k = array_keys($chars, $code[$i]); 260 | $check += ($k[0] * $p); 261 | $p++; 262 | if ($p > 20) { 263 | $p = 1; 264 | } 265 | } 266 | $check %= 47; 267 | $c = $chars[$check]; 268 | $code .= $c; 269 | 270 | // calculate check digit K 271 | $p = 1; 272 | $check = 0; 273 | for ($i = $len; $i >= 0; $i--) { 274 | $k = array_keys($chars, $code[$i]); 275 | $check += ($k[0] * $p); 276 | $p++; 277 | if ($p > 15) { 278 | $p = 1; 279 | } 280 | } 281 | $check %= 47; 282 | $k = $chars[$check]; 283 | 284 | return $c.$k; 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/Types/TypeEan13.php: -------------------------------------------------------------------------------- 1 | length; 41 | 42 | $dataLength = $length - 1; 43 | 44 | // Add zero padding in front 45 | $code = str_pad($code, $dataLength, '0', STR_PAD_LEFT); 46 | 47 | $checksumDigit = $this->calculateChecksumDigit($code); 48 | 49 | if (strlen($code) === $dataLength) { 50 | $code .= $checksumDigit; 51 | } elseif ($checksumDigit !== (int) $code[$dataLength]) { 52 | // If length of given barcode is same as final length, barcode is including checksum 53 | // Make sure that checksum is the same as we calculated 54 | throw new InvalidCheckDigitException; 55 | } 56 | 57 | if ($this->upca || $this->upce) { 58 | $code = '0'.$code; 59 | $length++; 60 | } 61 | 62 | if ($this->upce) { 63 | // convert UPC-A to UPC-E 64 | $tmp = substr($code, 4, 3); 65 | if (($tmp === '000') || ($tmp === '100') || ($tmp === '200')) { 66 | // manufacturer code ends in 000, 100, or 200 67 | $upce_code = substr($code, 2, 2).substr($code, 9, 3).$code[4]; 68 | } else { 69 | $tmp = substr($code, 5, 2); 70 | if ($tmp === '00') { 71 | // manufacturer code ends in 00 72 | $upce_code = substr($code, 2, 3).substr($code, 10, 2).'3'; 73 | } else { 74 | $tmp = $code[6]; 75 | if ($tmp === '0') { 76 | // manufacturer code ends in 0 77 | $upce_code = substr($code, 2, 4).$code[11].'4'; 78 | } else { 79 | // manufacturer code does not end in zero 80 | $upce_code = substr($code, 2, 5).$code[11]; 81 | } 82 | } 83 | } 84 | } 85 | 86 | // Convert digits to bars 87 | $codes = [ 88 | 'A' => [ // left odd parity 89 | '0' => '0001101', 90 | '1' => '0011001', 91 | '2' => '0010011', 92 | '3' => '0111101', 93 | '4' => '0100011', 94 | '5' => '0110001', 95 | '6' => '0101111', 96 | '7' => '0111011', 97 | '8' => '0110111', 98 | '9' => '0001011', 99 | ], 100 | 'B' => [ // left even parity 101 | '0' => '0100111', 102 | '1' => '0110011', 103 | '2' => '0011011', 104 | '3' => '0100001', 105 | '4' => '0011101', 106 | '5' => '0111001', 107 | '6' => '0000101', 108 | '7' => '0010001', 109 | '8' => '0001001', 110 | '9' => '0010111', 111 | ], 112 | 'C' => [ // right 113 | '0' => '1110010', 114 | '1' => '1100110', 115 | '2' => '1101100', 116 | '3' => '1000010', 117 | '4' => '1011100', 118 | '5' => '1001110', 119 | '6' => '1010000', 120 | '7' => '1000100', 121 | '8' => '1001000', 122 | '9' => '1110100', 123 | ], 124 | ]; 125 | 126 | $parities = [ 127 | '0' => ['A', 'A', 'A', 'A', 'A', 'A'], 128 | '1' => ['A', 'A', 'B', 'A', 'B', 'B'], 129 | '2' => ['A', 'A', 'B', 'B', 'A', 'B'], 130 | '3' => ['A', 'A', 'B', 'B', 'B', 'A'], 131 | '4' => ['A', 'B', 'A', 'A', 'B', 'B'], 132 | '5' => ['A', 'B', 'B', 'A', 'A', 'B'], 133 | '6' => ['A', 'B', 'B', 'B', 'A', 'A'], 134 | '7' => ['A', 'B', 'A', 'B', 'A', 'B'], 135 | '8' => ['A', 'B', 'A', 'B', 'B', 'A'], 136 | '9' => ['A', 'B', 'B', 'A', 'B', 'A'], 137 | ]; 138 | 139 | $upce_parities = [ 140 | [ 141 | '0' => ['B', 'B', 'B', 'A', 'A', 'A'], 142 | '1' => ['B', 'B', 'A', 'B', 'A', 'A'], 143 | '2' => ['B', 'B', 'A', 'A', 'B', 'A'], 144 | '3' => ['B', 'B', 'A', 'A', 'A', 'B'], 145 | '4' => ['B', 'A', 'B', 'B', 'A', 'A'], 146 | '5' => ['B', 'A', 'A', 'B', 'B', 'A'], 147 | '6' => ['B', 'A', 'A', 'A', 'B', 'B'], 148 | '7' => ['B', 'A', 'B', 'A', 'B', 'A'], 149 | '8' => ['B', 'A', 'B', 'A', 'A', 'B'], 150 | '9' => ['B', 'A', 'A', 'B', 'A', 'B'], 151 | ], 152 | [ 153 | '0' => ['A', 'A', 'A', 'B', 'B', 'B'], 154 | '1' => ['A', 'A', 'B', 'A', 'B', 'B'], 155 | '2' => ['A', 'A', 'B', 'B', 'A', 'B'], 156 | '3' => ['A', 'A', 'B', 'B', 'B', 'A'], 157 | '4' => ['A', 'B', 'A', 'A', 'B', 'B'], 158 | '5' => ['A', 'B', 'B', 'A', 'A', 'B'], 159 | '6' => ['A', 'B', 'B', 'B', 'A', 'A'], 160 | '7' => ['A', 'B', 'A', 'B', 'A', 'B'], 161 | '8' => ['A', 'B', 'A', 'B', 'B', 'A'], 162 | '9' => ['A', 'B', 'B', 'A', 'B', 'A'], 163 | ], 164 | ]; 165 | 166 | $seq = '101'; // left guard bar 167 | if ($this->upce) { 168 | $barcode = new Barcode($upce_code); 169 | $p = $upce_parities[$code[1]][$checksumDigit]; 170 | for ($i = 0; $i < 6; $i++) { 171 | $seq .= $codes[$p[$i]][$upce_code[$i]]; 172 | } 173 | $seq .= '010101'; // right guard bar 174 | } else { 175 | $barcode = new Barcode($code); 176 | $half_len = (int) ceil($length / 2); 177 | if ($length === 8) { 178 | for ($i = 0; $i < $half_len; $i++) { 179 | $seq .= $codes['A'][$code[$i]]; 180 | } 181 | } else { 182 | $p = $parities[$code[0]]; 183 | for ($i = 1; $i < $half_len; $i++) { 184 | $seq .= $codes[$p[$i - 1]][$code[$i]]; 185 | } 186 | } 187 | $seq .= '01010'; // center guard bar 188 | for ($i = $half_len; $i < $length; $i++) { 189 | if (! isset($codes['C'][$code[$i]])) { 190 | throw new InvalidCharacterException('Char '.$code[$i].' not allowed'); 191 | } 192 | $seq .= $codes['C'][$code[$i]]; 193 | } 194 | $seq .= '101'; // right guard bar 195 | } 196 | 197 | return BinarySequenceConverter::generate($seq, $barcode); 198 | } 199 | 200 | protected function calculateChecksumDigit(string $code): int 201 | { 202 | // calculate check digit 203 | $sum_a = 0; 204 | for ($i = 1; $i < $this->length - 1; $i += 2) { 205 | $sum_a += (int) $code[$i]; 206 | } 207 | if ($this->length > 12) { 208 | $sum_a *= 3; 209 | } 210 | $sum_b = 0; 211 | for ($i = 0; $i < $this->length - 1; $i += 2) { 212 | $sum_b += (int) $code[$i]; 213 | } 214 | if ($this->length < 13) { 215 | $sum_b *= 3; 216 | } 217 | $checksumDigit = ($sum_a + $sum_b) % 10; 218 | if ($checksumDigit > 0) { 219 | $checksumDigit = (10 - $checksumDigit); 220 | } 221 | 222 | return $checksumDigit; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/Types/TypeIntelligentMailBarcode.php: -------------------------------------------------------------------------------- 1 |
  • The Barcode Identifier shall be assigned by USPS to encode the 14 | * presort identification that is currently printed in human-readable form on the optional endorsement line (OEL) 15 | * as well as for future USPS use. This shall be two digits, with the second digit in the range of 0–4. The 16 | * allowable encoding ranges shall be 00–04, 10–14, 20–24, 30–34, 40–44, 50–54, 60–64, 70–74, 80–84, and 17 | * 90–94.
  • The Service Type Identifier shall be assigned by USPS for any combination of services requested 18 | * on the mailpiece. The allowable encoding range shall be 000http://it2.php.net/manual/en/function.dechex.php–999. 19 | * Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each 20 | * service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier 21 | * values.
  • The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digits number 22 | * that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000- 23 | * 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.
  • The 24 | * Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The 25 | * allowable encoding range shall be 000000000–999999999 when used with a 6 digit Mailer ID and 000000-999999 when 26 | * used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the 27 | * mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be 28 | * 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 00000–99999, 000000000–999999999, 29 | * and 00000000000–99999999999.
  • 30 | * 31 | * code to print, separate the ZIP (routing code) from the rest using a minus char '-' 32 | * (BarcodeID_ServiceTypeID_MailerID_SerialNumber-RoutingCode) 33 | */ 34 | 35 | class TypeIntelligentMailBarcode implements TypeInterface 36 | { 37 | /** 38 | * @throws BarcodeException 39 | */ 40 | public function getBarcodeData(string $code): Barcode 41 | { 42 | $asc_chr = [ 43 | 4, 44 | 0, 45 | 2, 46 | 6, 47 | 3, 48 | 5, 49 | 1, 50 | 9, 51 | 8, 52 | 7, 53 | 1, 54 | 2, 55 | 0, 56 | 6, 57 | 4, 58 | 8, 59 | 2, 60 | 9, 61 | 5, 62 | 3, 63 | 0, 64 | 1, 65 | 3, 66 | 7, 67 | 4, 68 | 6, 69 | 8, 70 | 9, 71 | 2, 72 | 0, 73 | 5, 74 | 1, 75 | 9, 76 | 4, 77 | 3, 78 | 8, 79 | 6, 80 | 7, 81 | 1, 82 | 2, 83 | 4, 84 | 3, 85 | 9, 86 | 5, 87 | 7, 88 | 8, 89 | 3, 90 | 0, 91 | 2, 92 | 1, 93 | 4, 94 | 0, 95 | 9, 96 | 1, 97 | 7, 98 | 0, 99 | 2, 100 | 4, 101 | 6, 102 | 3, 103 | 7, 104 | 1, 105 | 9, 106 | 5, 107 | 8, 108 | ]; 109 | $dsc_chr = [ 110 | 7, 111 | 1, 112 | 9, 113 | 5, 114 | 8, 115 | 0, 116 | 2, 117 | 4, 118 | 6, 119 | 3, 120 | 5, 121 | 8, 122 | 9, 123 | 7, 124 | 3, 125 | 0, 126 | 6, 127 | 1, 128 | 7, 129 | 4, 130 | 6, 131 | 8, 132 | 9, 133 | 2, 134 | 5, 135 | 1, 136 | 7, 137 | 5, 138 | 4, 139 | 3, 140 | 8, 141 | 7, 142 | 6, 143 | 0, 144 | 2, 145 | 5, 146 | 4, 147 | 9, 148 | 3, 149 | 0, 150 | 1, 151 | 6, 152 | 8, 153 | 2, 154 | 0, 155 | 4, 156 | 5, 157 | 9, 158 | 6, 159 | 7, 160 | 5, 161 | 2, 162 | 6, 163 | 3, 164 | 8, 165 | 5, 166 | 1, 167 | 9, 168 | 8, 169 | 7, 170 | 4, 171 | 0, 172 | 2, 173 | 6, 174 | 3, 175 | ]; 176 | $asc_pos = [ 177 | 3, 178 | 0, 179 | 8, 180 | 11, 181 | 1, 182 | 12, 183 | 8, 184 | 11, 185 | 10, 186 | 6, 187 | 4, 188 | 12, 189 | 2, 190 | 7, 191 | 9, 192 | 6, 193 | 7, 194 | 9, 195 | 2, 196 | 8, 197 | 4, 198 | 0, 199 | 12, 200 | 7, 201 | 10, 202 | 9, 203 | 0, 204 | 7, 205 | 10, 206 | 5, 207 | 7, 208 | 9, 209 | 6, 210 | 8, 211 | 2, 212 | 12, 213 | 1, 214 | 4, 215 | 2, 216 | 0, 217 | 1, 218 | 5, 219 | 4, 220 | 6, 221 | 12, 222 | 1, 223 | 0, 224 | 9, 225 | 4, 226 | 7, 227 | 5, 228 | 10, 229 | 2, 230 | 6, 231 | 9, 232 | 11, 233 | 2, 234 | 12, 235 | 6, 236 | 7, 237 | 5, 238 | 11, 239 | 0, 240 | 3, 241 | 2, 242 | ]; 243 | $dsc_pos = [ 244 | 2, 245 | 10, 246 | 12, 247 | 5, 248 | 9, 249 | 1, 250 | 5, 251 | 4, 252 | 3, 253 | 9, 254 | 11, 255 | 5, 256 | 10, 257 | 1, 258 | 6, 259 | 3, 260 | 4, 261 | 1, 262 | 10, 263 | 0, 264 | 2, 265 | 11, 266 | 8, 267 | 6, 268 | 1, 269 | 12, 270 | 3, 271 | 8, 272 | 6, 273 | 4, 274 | 4, 275 | 11, 276 | 0, 277 | 6, 278 | 1, 279 | 9, 280 | 11, 281 | 5, 282 | 3, 283 | 7, 284 | 3, 285 | 10, 286 | 7, 287 | 11, 288 | 8, 289 | 2, 290 | 10, 291 | 3, 292 | 5, 293 | 8, 294 | 0, 295 | 3, 296 | 12, 297 | 11, 298 | 8, 299 | 4, 300 | 5, 301 | 1, 302 | 3, 303 | 0, 304 | 7, 305 | 12, 306 | 9, 307 | 8, 308 | 10, 309 | ]; 310 | $code_arr = explode('-', $code); 311 | $tracking_number = $code_arr[0]; 312 | $routing_code = $code_arr[1] ?? ''; 313 | // Conversion of Routing Code 314 | $binary_code = match (strlen($routing_code)) { 315 | 0 => 0, 316 | 5 => bcadd($routing_code, '1'), 317 | 9 => bcadd($routing_code, '100001'), 318 | 11 => bcadd($routing_code, '1000100001'), 319 | default => throw new BarcodeException('Routing code unknown'), 320 | }; 321 | 322 | $binary_code = bcmul($binary_code, 10); 323 | $binary_code = bcadd($binary_code, $tracking_number[0]); 324 | $binary_code = bcmul($binary_code, 5); 325 | $binary_code = bcadd($binary_code, $tracking_number[1]); 326 | $binary_code .= substr($tracking_number, 2, 18); 327 | 328 | // convert to hexadecimal 329 | $binary_code = $this->dec_to_hex($binary_code); 330 | 331 | // pad to get 13 bytes 332 | $binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT); 333 | 334 | // convert string to array of bytes 335 | $binary_code_arr = chunk_split($binary_code, 2, "\r"); 336 | $binary_code_arr = substr($binary_code_arr, 0, -1); 337 | $binary_code_arr = explode("\r", $binary_code_arr); 338 | 339 | // calculate frame check sequence 340 | $fcs = $this->imb_crc11fcs($binary_code_arr); 341 | 342 | // exclude first 2 bits from first byte 343 | $first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2)); 344 | $binary_code_102bit = $first_byte.substr($binary_code, 2); 345 | 346 | // convert binary data to codewords 347 | $codewords = []; 348 | $data = $this->hex_to_dec($binary_code_102bit); 349 | $codewords[0] = bcmod($data, 636) * 2; 350 | $data = bcdiv($data, 636); 351 | for ($i = 1; $i < 9; $i++) { 352 | $codewords[$i] = bcmod($data, 1365); 353 | $data = bcdiv($data, 1365); 354 | } 355 | $codewords[9] = $data; 356 | if (($fcs >> 10) === 1) { 357 | $codewords[9] += 659; 358 | } 359 | 360 | // generate lookup tables 361 | $table2of13 = $this->imb_tables(2, 78); 362 | $table5of13 = $this->imb_tables(5, 1287); 363 | 364 | // convert codewords to characters 365 | $characters = []; 366 | $bitmask = 512; 367 | foreach ($codewords as $val) { 368 | if ($val <= 1286) { 369 | $chrcode = $table5of13[$val]; 370 | } else { 371 | $chrcode = $table2of13[($val - 1287)]; 372 | } 373 | if (($fcs & $bitmask) > 0) { 374 | // bitwise invert 375 | $chrcode = ((~$chrcode) & 8191); 376 | } 377 | $characters[] = $chrcode; 378 | $bitmask /= 2; 379 | } 380 | $characters = array_reverse($characters); 381 | 382 | // build bars 383 | $barcode = new Barcode($code); 384 | for ($i = 0; $i < 65; $i++) { 385 | $asc = (($characters[$asc_chr[$i]] & (2 ** $asc_pos[$i])) > 0); 386 | $dsc = (($characters[$dsc_chr[$i]] & (2 ** $dsc_pos[$i])) > 0); 387 | if ($asc && $dsc) { 388 | // full bar (F) 389 | $p = 0; 390 | $h = 3; 391 | } elseif ($asc) { 392 | // ascender (A) 393 | $p = 0; 394 | $h = 2; 395 | } elseif ($dsc) { 396 | // descender (D) 397 | $p = 1; 398 | $h = 2; 399 | } else { 400 | // tracker (T) 401 | $p = 1; 402 | $h = 1; 403 | } 404 | $barcode->addBar(new BarcodeBar(1, $h, true, $p)); 405 | if ($i < 64) { 406 | $barcode->addBar(new BarcodeBar(1, 2, false, 0)); 407 | } 408 | } 409 | 410 | return $barcode; 411 | } 412 | 413 | /** 414 | * Convert large integer number to hexadecimal representation. 415 | * (requires PHP bcmath extension) 416 | * 417 | * @param $number (string) number to convert specified as a string 418 | */ 419 | protected function dec_to_hex(string $number): string 420 | { 421 | if ($number === '0') { 422 | return '00'; 423 | } 424 | 425 | $hex = []; 426 | 427 | while ($number > 0) { 428 | $hex[] = strtoupper(dechex(bcmod($number, '16'))); 429 | $number = bcdiv($number, '16'); 430 | } 431 | $hex = array_reverse($hex); 432 | 433 | return implode($hex); 434 | } 435 | 436 | /** 437 | * Intelligent Mail Barcode calculation of Frame Check Sequence 438 | * 439 | * @param $code_arr array of hexadecimal values (13 bytes holding 102 bits right justified). 440 | * @return int 11 bit Frame Check Sequence as integer (decimal base) 441 | */ 442 | protected function imb_crc11fcs(array $code_arr): int 443 | { 444 | $genpoly = 0x0F35; // generator polynomial 445 | $fcs = 0x07FF; // Frame Check Sequence 446 | // do most significant byte skipping the 2 most significant bits 447 | $data = hexdec($code_arr[0]) << 5; 448 | for ($bit = 2; $bit < 8; $bit++) { 449 | if (($fcs ^ $data) & 0x400) { 450 | $fcs = ($fcs << 1) ^ $genpoly; 451 | } else { 452 | $fcs <<= 1; 453 | } 454 | $fcs &= 0x7FF; 455 | $data <<= 1; 456 | } 457 | // do rest of bytes 458 | for ($byte = 1; $byte < 13; $byte++) { 459 | $data = hexdec($code_arr[$byte]) << 3; 460 | for ($bit = 0; $bit < 8; $bit++) { 461 | if (($fcs ^ $data) & 0x400) { 462 | $fcs = ($fcs << 1) ^ $genpoly; 463 | } else { 464 | $fcs <<= 1; 465 | } 466 | $fcs &= 0x7FF; 467 | $data <<= 1; 468 | } 469 | } 470 | 471 | return $fcs; 472 | } 473 | 474 | /** 475 | * Convert large hexadecimal number to decimal representation (string). 476 | * (requires PHP bcmath extension) 477 | * 478 | * @param $hex (string) hexadecimal number to convert specified as a string 479 | * @return int|string hexadecimal representation 480 | */ 481 | protected function hex_to_dec(string $hex): int|string 482 | { 483 | $dec = 0; 484 | $bitval = 1; 485 | $len = strlen($hex); 486 | for ($pos = ($len - 1); $pos >= 0; $pos--) { 487 | $dec = bcadd($dec, bcmul(hexdec($hex[$pos]), $bitval)); 488 | $bitval = bcmul($bitval, 16); 489 | } 490 | 491 | return $dec; 492 | } 493 | 494 | /** 495 | * generate Nof13 tables used for Intelligent Mail Barcode 496 | * 497 | * @param $n (int) is the type of table: 2 for 2of13 table, 5 for 5of13table 498 | * @param $size (int) size of table (78 for n=2 and 1287 for n=5) 499 | */ 500 | protected function imb_tables(int $n, int $size): array 501 | { 502 | $table = []; 503 | $lli = 0; // LUT lower index 504 | $lui = $size - 1; // LUT upper index 505 | for ($count = 0; $count < 8192; $count++) { 506 | $bit_count = 0; 507 | for ($bit_index = 0; $bit_index < 13; $bit_index++) { 508 | $bit_count += (int) (($count & (1 << $bit_index)) !== 0); 509 | } 510 | // if we don't have the right number of bits on, go on to the next value 511 | if ($bit_count === $n) { 512 | $reverse = ($this->imb_reverse_us($count) >> 3); 513 | // if the reverse is less than count, we have already visited this pair before 514 | if ($reverse >= $count) { 515 | // If count is symmetric, place it at the first free slot from the end of the list. 516 | // Otherwise, place it at the first free slot from the beginning of the list && place $reverse ath the next free slot from the beginning of the list 517 | if ($reverse === $count) { 518 | $table[$lui] = $count; 519 | $lui--; 520 | } else { 521 | $table[$lli] = $count; 522 | $lli++; 523 | $table[$lli] = $reverse; 524 | $lli++; 525 | } 526 | } 527 | } 528 | } 529 | 530 | return $table; 531 | } 532 | 533 | /** 534 | * Reverse unsigned short value 535 | * 536 | * @param $num (int) value to reverse 537 | */ 538 | protected function imb_reverse_us(int $num): int 539 | { 540 | $rev = 0; 541 | for ($i = 0; $i < 16; $i++) { 542 | $rev <<= 1; 543 | $rev |= ($num & 1); 544 | $num >>= 1; 545 | } 546 | 547 | return $rev; 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /src/Types/TypeInterface.php: -------------------------------------------------------------------------------- 1 | getChecksum($code); 38 | 39 | if ((strlen($code) % 2) != 0) { 40 | // add leading zero if code-length is odd 41 | $code = '0'.$code; 42 | } 43 | // add start and stop codes 44 | $code = 'AA'.strtolower($code).'ZA'; 45 | 46 | $barcode = new Barcode($code); 47 | for ($i = 0, $iMax = strlen($code); $i < $iMax; $i = ($i + 2)) { 48 | $char_bar = $code[$i]; 49 | $char_space = $code[$i + 1]; 50 | if (! isset($chr[$char_bar], $chr[$char_space])) { 51 | throw new InvalidCharacterException; 52 | } 53 | 54 | // create a bar-space sequence 55 | $seq = ''; 56 | $chrlen = strlen($chr[$char_bar]); 57 | for ($s = 0; $s < $chrlen; $s++) { 58 | $seq .= $chr[$char_bar][$s].$chr[$char_space][$s]; 59 | } 60 | 61 | for ($j = 0, $jMax = strlen($seq); $j < $jMax; $j++) { 62 | if (($j % 2) === 0) { 63 | $t = true; // bar 64 | } else { 65 | $t = false; // space 66 | } 67 | $w = $seq[$j]; 68 | $barcode->addBar(new BarcodeBar($w, 1, $t)); 69 | } 70 | } 71 | 72 | return $barcode; 73 | } 74 | 75 | protected function getChecksum(string $code): string 76 | { 77 | return (string) BarcodeHelper::getChecksum($code); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Types/TypeKix.php: -------------------------------------------------------------------------------- 1 | checksum) { 44 | // add checksum 45 | $clen = strlen($code); 46 | $p = 2; 47 | $check = 0; 48 | for ($i = ($clen - 1); $i >= 0; $i--) { 49 | $check += (hexdec($code[$i]) * $p); 50 | $p++; 51 | if ($p > 7) { 52 | $p = 2; 53 | } 54 | } 55 | $check %= 11; 56 | if ($check > 0) { 57 | $check = 11 - $check; 58 | } 59 | $code .= $check; 60 | } 61 | $seq = '110'; // left guard 62 | $clen = strlen($code); 63 | for ($i = 0; $i < $clen; $i++) { 64 | $digit = $code[$i]; 65 | if (! isset($chr[$digit])) { 66 | throw new InvalidCharacterException('Char '.$digit.' is unsupported'); 67 | } 68 | $seq .= $chr[$digit]; 69 | } 70 | $seq .= '1001'; // right guard 71 | 72 | return BinarySequenceConverter::convert($code, $seq); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Types/TypePharmacode.php: -------------------------------------------------------------------------------- 1 | 0) { 21 | if (($code % 2) === 0) { 22 | $seq .= '11100'; 23 | $code -= 2; 24 | } else { 25 | $seq .= '100'; 26 | $code--; 27 | } 28 | $code /= 2; 29 | } 30 | 31 | $seq = substr($seq, 0, -2); 32 | $seq = strrev($seq); 33 | 34 | return BinarySequenceConverter::convert($code, $seq); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Types/TypePharmacodeTwoCode.php: -------------------------------------------------------------------------------- 1 | addBar(new BarcodeBar(1, $h, 1, $p)); 71 | if ($i < (strlen($seq) - 1)) { 72 | $barcode->addBar(new BarcodeBar(1, 2, 0, 0)); 73 | } 74 | } 75 | 76 | return $barcode; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Types/TypePlanet.php: -------------------------------------------------------------------------------- 1 | [1, 1, 2, 2, 2], 18 | 1 => [2, 2, 2, 1, 1], 19 | 2 => [2, 2, 1, 2, 1], 20 | 3 => [2, 2, 1, 1, 2], 21 | 4 => [2, 1, 2, 2, 1], 22 | 5 => [2, 1, 2, 1, 2], 23 | 6 => [2, 1, 1, 2, 2], 24 | 7 => [1, 2, 2, 2, 1], 25 | 8 => [1, 2, 2, 1, 2], 26 | 9 => [1, 2, 1, 2, 2], 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /src/Types/TypePostnet.php: -------------------------------------------------------------------------------- 1 | [2, 2, 1, 1, 1], 21 | 1 => [1, 1, 1, 2, 2], 22 | 2 => [1, 1, 2, 1, 2], 23 | 3 => [1, 1, 2, 2, 1], 24 | 4 => [1, 2, 1, 1, 2], 25 | 5 => [1, 2, 1, 2, 1], 26 | 6 => [1, 2, 2, 1, 1], 27 | 7 => [2, 1, 1, 1, 2], 28 | 8 => [2, 1, 1, 2, 1], 29 | 9 => [2, 1, 2, 1, 1], 30 | ]; 31 | 32 | public function getBarcodeData(string $code): Barcode 33 | { 34 | $code = str_replace(['-', ' '], '', $code); 35 | $len = strlen($code); 36 | 37 | $barcode = new Barcode($code); 38 | 39 | // calculate checksum 40 | $sum = 0; 41 | for ($i = 0; $i < $len; $i++) { 42 | $sum += (int) $code[$i]; 43 | } 44 | $chkd = ($sum % 10); 45 | if ($chkd > 0) { 46 | $chkd = (10 - $chkd); 47 | } 48 | $code .= $chkd; 49 | $len = strlen($code); 50 | 51 | // start bar 52 | $barcode->addBar(new BarcodeBar(1, 2, 1)); 53 | $barcode->addBar(new BarcodeBar(1, 2, 0)); 54 | 55 | for ($i = 0; $i < $len; $i++) { 56 | for ($j = 0; $j < 5; $j++) { 57 | $h = $this->barlen[$code[$i]][$j]; 58 | $p = floor(1 / $h); 59 | $barcode->addBar(new BarcodeBar(1, $h, 1, $p)); 60 | $barcode->addBar(new BarcodeBar(1, 2, 0)); 61 | } 62 | } 63 | 64 | // end bar 65 | $barcode->addBar(new BarcodeBar(1, 2, 1)); 66 | 67 | return $barcode; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Types/TypeRms4cc.php: -------------------------------------------------------------------------------- 1 | [3, 3, 2, 2], 29 | '1' => [3, 4, 1, 2], 30 | '2' => [3, 4, 2, 1], 31 | '3' => [4, 3, 1, 2], 32 | '4' => [4, 3, 2, 1], 33 | '5' => [4, 4, 1, 1], 34 | '6' => [3, 1, 4, 2], 35 | '7' => [3, 2, 3, 2], 36 | '8' => [3, 2, 4, 1], 37 | '9' => [4, 1, 3, 2], 38 | 'A' => [4, 1, 4, 1], 39 | 'B' => [4, 2, 3, 1], 40 | 'C' => [3, 1, 2, 4], 41 | 'D' => [3, 2, 1, 4], 42 | 'E' => [3, 2, 2, 3], 43 | 'F' => [4, 1, 1, 4], 44 | 'G' => [4, 1, 2, 3], 45 | 'H' => [4, 2, 1, 3], 46 | 'I' => [1, 3, 4, 2], 47 | 'J' => [1, 4, 3, 2], 48 | 'K' => [1, 4, 4, 1], 49 | 'L' => [2, 3, 3, 2], 50 | 'M' => [2, 3, 4, 1], 51 | 'N' => [2, 4, 3, 1], 52 | 'O' => [1, 3, 2, 4], 53 | 'P' => [1, 4, 1, 4], 54 | 'Q' => [1, 4, 2, 3], 55 | 'R' => [2, 3, 1, 4], 56 | 'S' => [2, 3, 2, 3], 57 | 'T' => [2, 4, 1, 3], 58 | 'U' => [1, 1, 4, 4], 59 | 'V' => [1, 2, 3, 4], 60 | 'W' => [1, 2, 4, 3], 61 | 'X' => [2, 1, 3, 4], 62 | 'Y' => [2, 1, 4, 3], 63 | 'Z' => [2, 2, 3, 3], 64 | ]; 65 | 66 | $code = strtoupper($code); 67 | $len = strlen($code); 68 | 69 | $barcode = new Barcode($code); 70 | 71 | if (! $this->kix) { 72 | // table for checksum calculation (row,col) 73 | $checktable = [ 74 | '0' => [1, 1], 75 | '1' => [1, 2], 76 | '2' => [1, 3], 77 | '3' => [1, 4], 78 | '4' => [1, 5], 79 | '5' => [1, 0], 80 | '6' => [2, 1], 81 | '7' => [2, 2], 82 | '8' => [2, 3], 83 | '9' => [2, 4], 84 | 'A' => [2, 5], 85 | 'B' => [2, 0], 86 | 'C' => [3, 1], 87 | 'D' => [3, 2], 88 | 'E' => [3, 3], 89 | 'F' => [3, 4], 90 | 'G' => [3, 5], 91 | 'H' => [3, 0], 92 | 'I' => [4, 1], 93 | 'J' => [4, 2], 94 | 'K' => [4, 3], 95 | 'L' => [4, 4], 96 | 'M' => [4, 5], 97 | 'N' => [4, 0], 98 | 'O' => [5, 1], 99 | 'P' => [5, 2], 100 | 'Q' => [5, 3], 101 | 'R' => [5, 4], 102 | 'S' => [5, 5], 103 | 'T' => [5, 0], 104 | 'U' => [0, 1], 105 | 'V' => [0, 2], 106 | 'W' => [0, 3], 107 | 'X' => [0, 4], 108 | 'Y' => [0, 5], 109 | 'Z' => [0, 0], 110 | ]; 111 | 112 | $row = 0; 113 | $col = 0; 114 | for ($i = 0; $i < $len; $i++) { 115 | $row += $checktable[$code[$i]][0]; 116 | $col += $checktable[$code[$i]][1]; 117 | } 118 | $row %= 6; 119 | $col %= 6; 120 | $chk = array_keys($checktable, [$row, $col]); 121 | $code .= $chk[0]; 122 | $len++; 123 | 124 | // start bar 125 | $barcode->addBar(new BarcodeBar(1, 2, 1)); 126 | $barcode->addBar(new BarcodeBar(1, 2, 0)); 127 | } 128 | 129 | for ($i = 0; $i < $len; $i++) { 130 | for ($j = 0; $j < 4; $j++) { 131 | switch ($barmode[$code[$i]][$j]) { 132 | case 1: 133 | $p = 0; 134 | $h = 2; 135 | break; 136 | 137 | case 2: 138 | $p = 0; 139 | $h = 3; 140 | break; 141 | 142 | case 3: 143 | $p = 1; 144 | $h = 1; 145 | break; 146 | 147 | case 4: 148 | $p = 1; 149 | $h = 2; 150 | break; 151 | } 152 | 153 | $barcode->addBar(new BarcodeBar(1, $h, 1, $p)); 154 | $barcode->addBar(new BarcodeBar(1, 2, 0)); 155 | } 156 | } 157 | 158 | if (! $this->kix) { 159 | // stop bar 160 | $barcode->addBar(new BarcodeBar(1, 3, 1)); 161 | } 162 | 163 | return $barcode; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/Types/TypeStandard2of5.php: -------------------------------------------------------------------------------- 1 | checksum) { 36 | // add checksum 37 | $code .= $this->checksum_s25($code); 38 | } 39 | $seq = '11011010'; 40 | 41 | for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) { 42 | $digit = $code[$i]; 43 | if (! isset($chr[$digit])) { 44 | throw new InvalidCharacterException('Char '.$digit.' is unsupported'); 45 | } 46 | $seq .= $chr[$digit]; 47 | } 48 | $seq .= '1101011'; 49 | 50 | return BinarySequenceConverter::convert($code, $seq); 51 | } 52 | 53 | /** 54 | * Checksum for standard 2 of 5 barcodes. 55 | * 56 | * @param $code (string) code to process. 57 | * @return int checksum. 58 | */ 59 | protected function checksum_s25(string $code): int 60 | { 61 | return BarcodeHelper::getChecksum($code); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Types/TypeStandard2of5Checksum.php: -------------------------------------------------------------------------------- 1 | length; 25 | 26 | // Padding 27 | $code = str_pad($code, $len, '0', STR_PAD_LEFT); 28 | 29 | // calculate check digit 30 | if ($len === 2) { 31 | $r = (int) $code % 4; 32 | } elseif ($len === 5) { 33 | $r = (3 * ((int) $code[0] + (int) $code[2] + (int) $code[4])) + (9 * ((int) $code[1] + (int) $code[3])); 34 | $r %= 10; 35 | } else { 36 | throw new InvalidCheckDigitException; 37 | } 38 | 39 | // Convert digits to bars 40 | $codes = [ 41 | 'A' => [ // left odd parity 42 | '0' => '0001101', 43 | '1' => '0011001', 44 | '2' => '0010011', 45 | '3' => '0111101', 46 | '4' => '0100011', 47 | '5' => '0110001', 48 | '6' => '0101111', 49 | '7' => '0111011', 50 | '8' => '0110111', 51 | '9' => '0001011', 52 | ], 53 | 'B' => [ // left even parity 54 | '0' => '0100111', 55 | '1' => '0110011', 56 | '2' => '0011011', 57 | '3' => '0100001', 58 | '4' => '0011101', 59 | '5' => '0111001', 60 | '6' => '0000101', 61 | '7' => '0010001', 62 | '8' => '0001001', 63 | '9' => '0010111', 64 | ], 65 | ]; 66 | 67 | $parities = [ 68 | 2 => [ 69 | '0' => ['A', 'A'], 70 | '1' => ['A', 'B'], 71 | '2' => ['B', 'A'], 72 | '3' => ['B', 'B'], 73 | ], 74 | 5 => [ 75 | '0' => ['B', 'B', 'A', 'A', 'A'], 76 | '1' => ['B', 'A', 'B', 'A', 'A'], 77 | '2' => ['B', 'A', 'A', 'B', 'A'], 78 | '3' => ['B', 'A', 'A', 'A', 'B'], 79 | '4' => ['A', 'B', 'B', 'A', 'A'], 80 | '5' => ['A', 'A', 'B', 'B', 'A'], 81 | '6' => ['A', 'A', 'A', 'B', 'B'], 82 | '7' => ['A', 'B', 'A', 'B', 'A'], 83 | '8' => ['A', 'B', 'A', 'A', 'B'], 84 | '9' => ['A', 'A', 'B', 'A', 'B'], 85 | ], 86 | ]; 87 | 88 | $p = $parities[$len][$r]; 89 | $seq = '1011'; // left guard bar 90 | $seq .= $codes[$p[0]][$code[0]]; 91 | for ($i = 1; $i < $len; $i++) { 92 | $seq .= '01'; // separator 93 | $seq .= $codes[$p[$i]][$code[$i]]; 94 | } 95 | 96 | return BinarySequenceConverter::convert($code, $seq); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Types/TypeUpcExtension5.php: -------------------------------------------------------------------------------- 1 |