allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s

This commit is contained in:
2026-03-30 14:54:57 +07:00
parent 66aed7c4e8
commit b5e3a778ce
21316 changed files with 2777892 additions and 13 deletions

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) AgeekDev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,74 @@
{
"name": "ageekdev/laravel-barcode",
"description": "laravel barcode generator",
"keywords": [
"ageekdev",
"laravel",
"barcode"
],
"homepage": "https://github.com/ageekdev/laravel-barcode",
"license": "MIT",
"authors": [
{
"name": "Tint Naing Win",
"email": "amigo.k8@gmail.com",
"role": "Developer"
}
],
"require": {
"php": "^8.1",
"ext-bcmath": "*",
"illuminate/contracts": "^9.0|^10.0|^11.0|^12.0",
"spatie/color": "^1.5"
},
"require-dev": {
"laravel/pint": "^1.5",
"larastan/larastan": "^2.0|^3.0",
"orchestra/testbench": "^7.31|^8.11|^9.0|^10.0",
"pestphp/pest": "^1.21|^2.0|^3.0",
"pestphp/pest-plugin-laravel": "^1.4|^2.0|^3.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan-deprecation-rules": "^1.0|^2.0",
"phpstan/phpstan-phpunit": "^1.0|^2.0",
"roave/security-advisories": "dev-latest"
},
"autoload": {
"psr-4": {
"AgeekDev\\Barcode\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"AgeekDev\\Barcode\\Tests\\": "tests"
}
},
"suggest": {
"ext-gd": "For JPG and PNG generators, GD or Imagick is required",
"ext-imagick": "For JPG and PNG generators, GD or Imagick is required"
},
"scripts": {
"analyse": "vendor/bin/phpstan analyse",
"test": "vendor/bin/pest",
"test-coverage": "vendor/bin/pest --coverage",
"format": "vendor/bin/pint"
},
"config": {
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"phpstan/extension-installer": true
}
},
"extra": {
"laravel": {
"providers": [
"AgeekDev\\Barcode\\BarcodeServiceProvider"
],
"aliases": {
"Barcode": "AgeekDev\\Barcode\\Facades\\Barcode"
}
}
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,47 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Barcode Image Type
|--------------------------------------------------------------------------
|
| This option controls the default barcode generator that will be used
| to generate barcode. Alternative barcode generators may be setup and used as
| needed; however, this generator will be used by default.
|
| Supported: "dynamic_html", "html", "jpg", "png", "svg"
|
*/
'image_type' => env('BARCODE_DRIVER', 'png'),
/*
|----------------------------------------------------------------------------------------------
| Accepted Barcode Types
|----------------------------------------------------------------------------------------------
| Supported: TYPE_CODE_32, TYPE_CODE_39, TYPE_CODE_39_CHECKSUM, TYPE_CODE_39E,
| TYPE_CODE_39E_CHECKSUM, TYPE_CODE_93, TYPE_STANDARD_2_5, TYPE_STANDARD_2_5_CHECKSUM,
| TYPE_INTERLEAVED_2_5, TYPE_INTERLEAVED_2_5_CHECKSUM, TYPE_CODE_128, TYPE_CODE_128_A,
| TYPE_CODE_128_B, TYPE_CODE_128_C, TYPE_EAN_2, TYPE_EAN_5, TYPE_EAN_8, TYPE_EAN_13,
| TYPE_UPC_A, TYPE_UPC_E, TYPE_MSI, TYPE_MSI_CHECKSUM, TYPE_POSTNET, TYPE_PLANET, TYPE_RMS4CC,
| TYPE_KIX, TYPE_IMB, TYPE_CODABAR, TYPE_CODE_11, TYPE_PHARMA_CODE, TYPE_PHARMA_CODE_TWO_TRACKS
|
*/
'type' => \AgeekDev\Barcode\Enums\BarcodeType::CODE_128,
/*
* Foreground color of the barcode
*/
'foreground_color' => '#000000',
/*
* width factor of barcode
*/
'width_factor' => 2,
/*
* Height of image barcode
*/
'height' => 30,
];

View File

@@ -0,0 +1,68 @@
<?php
namespace AgeekDev\Barcode;
use AgeekDev\Barcode\Contracts\ImageType;
use AgeekDev\Barcode\Enums\BarcodeType;
use AgeekDev\Barcode\Enums\Type;
use AgeekDev\Barcode\Types\TypeInterface;
abstract class AbstractGenerator implements ImageType
{
/**
* The foreground color of the barcode.
*/
protected string|array $foregroundColor = '#000000';
protected int $height;
protected int $widthFactor;
protected BarcodeType|Type $type;
public function __construct()
{
$this->foregroundColor = config('barcode.foreground_color');
$this->widthFactor = config('barcode.width_factor');
$this->height = config('barcode.height');
$this->type = config('barcode.type');
}
protected function getBarcodeData(string $code, BarcodeType|Type $type): Barcode
{
return $this->createDataBuilderForType($type)->getBarcodeData($code);
}
protected function createDataBuilderForType(BarcodeType|Type $type): TypeInterface
{
return $type->class();
}
public function type(BarcodeType|Type $type): static
{
$this->type = $type;
return $this;
}
public function foregroundColor(string $foregroundColor): static
{
$this->foregroundColor = $foregroundColor;
return $this;
}
public function height(int $height): static
{
$this->height = $height;
return $this;
}
public function widthFactor(int $widthFactor): static
{
$this->widthFactor = $widthFactor;
return $this;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace AgeekDev\Barcode;
class Barcode
{
protected string $barcode;
protected int $width = 0;
protected int $height = 0;
protected array $bars = [];
public function __construct(string $barcode)
{
$this->barcode = $barcode;
}
/**
* Add bar.
*/
public function addBar(BarcodeBar $bar): void
{
$this->bars[] = $bar;
$this->width += $bar->getWidth();
$this->height = max($this->height, $bar->getHeight());
}
/**
* Get the barcode.
*/
public function getBarcode(): string
{
return $this->barcode;
}
/**
* Get the width.
*/
public function getWidth(): int
{
return $this->width;
}
/**
* Get the height.
*/
public function getHeight(): int
{
return $this->height;
}
/**
* Get the bars.
*/
public function getBars(): array
{
return $this->bars;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace AgeekDev\Barcode;
class BarcodeBar
{
protected int $width;
protected int $height;
protected int $positionVertical;
protected int $type;
public const TYPE_BAR = 1;
public const TYPE_SPACING = 0;
public function __construct(int $width, int $height, bool $drawBar = true, int $positionVertical = 0)
{
$this->width = $width;
$this->height = $height;
$this->positionVertical = $positionVertical;
$this->type = $drawBar ? self::TYPE_BAR : self::TYPE_SPACING;
}
/**
* Get the width.
*/
public function getWidth(): int
{
return $this->width;
}
/**
* Get the height.
*/
public function getHeight(): int
{
return $this->height;
}
/**
* Get position vertical.
*/
public function getPositionVertical(): int
{
return $this->positionVertical;
}
/**
* Bar or not.
*/
public function isBar(): bool
{
return $this->type === self::TYPE_BAR;
}
}

View File

@@ -0,0 +1,277 @@
<?php
namespace AgeekDev\Barcode;
use AgeekDev\Barcode\Contracts\Factory;
use AgeekDev\Barcode\Contracts\ImageType;
use AgeekDev\Barcode\Drivers\DynamicHTML;
use AgeekDev\Barcode\Drivers\HTML;
use AgeekDev\Barcode\Drivers\JPG;
use AgeekDev\Barcode\Drivers\PNG;
use AgeekDev\Barcode\Drivers\SVG;
use AgeekDev\Barcode\Enums\BarcodeType;
use AgeekDev\Barcode\Enums\Type;
use AgeekDev\Barcode\Exceptions\BarcodeException;
use Closure;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Str;
use InvalidArgumentException;
class BarcodeManager implements Factory
{
/**
* The container instance.
*/
protected Container $container;
/**
* The configuration repository instance.
*
* @var \Illuminate\Contracts\Config\Repository
*/
protected mixed $config;
/**
* The registered custom driver creators.
*/
protected array $customCreators = [];
/**
* The array of created "drivers".
*/
protected array $imageTypes = [];
/**
* Create a new manager instance.
*
* @throws BindingResolutionException
*/
public function __construct(Container $container)
{
$this->container = $container;
$this->config = $container->make('config');
}
/**
* Get a image type instance.
*/
public function imageType(?string $name = null): ImageType
{
$name = $name ?: $this->getDefaultDriver();
// If the given driver has not been created before, we will create the instances
// here and cache it, so we can return it next time very quickly. If there is
// already a driver created by this name, we'll just return that instance.
if (! isset($this->imageTypes[$name])) {
$this->imageTypes[$name] = $this->createDriver($name);
}
return $this->imageTypes[$name];
}
/**
* Create a new driver instance.
*
* @throws InvalidArgumentException
*/
protected function createDriver(string $driver): ImageType
{
// First, we will determine if a custom driver creator exists for the given driver and
// if it does not we will check for a creator method for the driver. Custom creator
// callbacks allow developers to build their own "drivers" easily using Closures.
if (isset($this->customCreators[$driver])) {
return $this->callCustomCreator($driver);
}
$method = 'create'.Str::studly($driver).'Driver';
if (method_exists($this, $method)) {
return $this->$method();
}
throw new InvalidArgumentException("ImageType [$driver] not supported.");
}
/**
* Call a custom driver creator.
*/
protected function callCustomCreator(string $driver): ImageType
{
return $this->customCreators[$driver]($this->container);
}
/**
* Create an HTML instance.
*/
public function createHtmlDriver(): ImageType
{
return new HTML;
}
/**
* Create an DynamicHTML instance.
*/
public function createDynamicHtmlDriver(): ImageType
{
return new DynamicHTML;
}
/**
* Create an JPG instance.
*/
public function createJpgDriver(): ImageType
{
return new JPG;
}
/**
* Create an PNG instance.
*/
public function createPngDriver(): ImageType
{
return new PNG;
}
/**
* Create an SVG instance.
*/
public function createSvgDriver(): ImageType
{
return new SVG;
}
/**
* Generate Barcode.
*/
public function generate(string $text): string
{
return $this->imageType()->generate($text);
}
/**
* Set the barcode type.
*/
public function type(BarcodeType|Type $type): ImageType
{
return $this->imageType()->type($type);
}
/**
* Set the barcode foreground color.
*/
public function foregroundColor(string $foregroundColor): ImageType
{
return $this->imageType()->foregroundColor($foregroundColor);
}
/**
* Set the barcode height.
*/
public function height(int $height): ImageType
{
return $this->imageType()->height($height);
}
/**
* Set the barcode width factor.
*/
public function widthFactor(int $widthFactor): ImageType
{
return $this->imageType()->widthFactor($widthFactor);
}
/**
* Force the use of Imagick image extension
*
* @throws BarcodeException
*/
public function useImagick(): ImageType
{
if (method_exists($this->imageType(), 'useImagick')) {
if (! extension_loaded('imagick')) {
throw new BarcodeException('The imagick is not installed!');
}
return $this->imageType()->useImagick();
}
throw new BarcodeException('This image type does not support useImagick function.');
}
/**
* Force the use of the GD image library
*
* @throws BarcodeException
*/
public function useGd(): ImageType
{
if (method_exists($this->imageType(), 'useGd')) {
if (! function_exists('imagecreate')) {
throw new BarcodeException('The GD is not installed!');
}
return $this->imageType()->useGd();
}
throw new BarcodeException('This image type does not support useGd function.');
}
/**
* Get the default driver name.
*/
public function getDefaultDriver(): string
{
return $this->config->get('barcode.image_type', 'png');
}
/**
* Register a custom driver creator Closure.
*/
public function extend(string $imageType, Closure $callback): self
{
$this->customCreators[$imageType] = $callback;
return $this;
}
/**
* Get the container instance used by the manager.
*/
public function getContainer(): Container
{
return $this->container;
}
/**
* Set the container instance used by the manager.
*/
public function setContainer(Container $container): self
{
$this->container = $container;
return $this;
}
/**
* Unset the given disk instances.
*/
public function forgetImageType(array|string $imageType): self
{
foreach ((array) $imageType as $imageTypeName) {
unset($this->imageTypes[$imageTypeName]);
}
return $this;
}
/**
* Dynamically call the default driver instance.
*
* @return mixed
*/
public function __call(string $method, array $parameters)
{
return $this->imageType()->$method(...$parameters);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace AgeekDev\Barcode;
use Illuminate\Support\ServiceProvider;
class BarcodeServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*/
public function register(): void
{
$this->mergeConfigFrom(__DIR__.'/../config/barcode.php', 'barcode');
$this->app->bind('laravel-barcode', function ($app) {
return new BarcodeManager($app);
});
}
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/barcode.php' => config_path('barcode.php'),
], 'laravel-barcode-config');
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace AgeekDev\Barcode\Contracts;
interface Factory
{
/**
* Get image type implementation.
*/
public function imageType(?string $name = null): ImageType;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace AgeekDev\Barcode\Contracts;
use AgeekDev\Barcode\Enums\BarcodeType;
use AgeekDev\Barcode\Enums\Type;
interface ImageType
{
public function generate(string $text): string;
public function type(BarcodeType|Type $type): static;
public function foregroundColor(string $foregroundColor): static;
public function height(int $height): static;
public function widthFactor(int $widthFactor): static;
}

View File

@@ -0,0 +1,44 @@
<?php
namespace AgeekDev\Barcode\Drivers;
use AgeekDev\Barcode\AbstractGenerator;
use AgeekDev\Barcode\BarcodeBar;
class DynamicHTML extends AbstractGenerator
{
private const WIDTH_PRECISION = 6;
/**
* Return an HTML representation of barcode.
* This 'dynamic' version uses percentage based widths and heights, resulting in a vector-y qualitative result.
*
* @param string $text code to print
*/
public function generate(string $text): string
{
$barcodeData = $this->getBarcodeData($text, $this->type);
$html = '<div style="font-size:0;position:relative;width:100%;height:100%">'.PHP_EOL;
$positionHorizontal = 0;
/** @var BarcodeBar $bar */
foreach ($barcodeData->getBars() as $bar) {
$barWidth = $bar->getWidth() / $barcodeData->getWidth() * 100;
$barHeight = round(($bar->getHeight() / $barcodeData->getHeight() * 100), 3);
if ($barWidth > 0 && $bar->isBar()) {
$positionVertical = round(($bar->getPositionVertical() / $barcodeData->getHeight() * 100), 3);
// draw a vertical bar
$html .= '<div style="background-color:'.$this->foregroundColor.';width:'.round($barWidth, self::WIDTH_PRECISION).'%;height:'.$barHeight.'%;position:absolute;left:'.round($positionHorizontal, self::WIDTH_PRECISION).'%;top:'.$positionVertical.(($positionVertical > 0) ? '%' : '').'">&nbsp;</div>'.PHP_EOL;
}
$positionHorizontal += $barWidth;
}
$html .= '</div>'.PHP_EOL;
return $html;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace AgeekDev\Barcode\Drivers;
use AgeekDev\Barcode\AbstractGenerator;
use AgeekDev\Barcode\BarcodeBar;
class HTML extends AbstractGenerator
{
/**
* Return an HTML representation of barcode.
* This original version uses pixel based widths and heights. Use Dynamic HTML version for better quality representation.
*/
public function generate(string $text): string
{
$barcodeData = $this->getBarcodeData($text, $this->type);
$html = '<div style="font-size:0;position:relative;width:'.($barcodeData->getWidth() * $this->widthFactor).'px;height:'.($this->height).'px;">'.PHP_EOL;
$positionHorizontal = 0;
/** @var BarcodeBar $bar */
foreach ($barcodeData->getBars() as $bar) {
$barWidth = round(($bar->getWidth() * $this->widthFactor), 3);
$barHeight = round(($bar->getHeight() * $this->height / $barcodeData->getHeight()), 3);
if ($barWidth > 0 && $bar->isBar()) {
$positionVertical = round(($bar->getPositionVertical() * $this->height / $barcodeData->getHeight()), 3);
$html .= '<div style="background-color:'.$this->foregroundColor.';width:'.$barWidth.'px;height:'.$barHeight.'px;position:absolute;left:'.$positionHorizontal.'px;top:'.$positionVertical.(($positionVertical > 0) ? 'px' : '').'">&nbsp;</div>'.PHP_EOL;
}
$positionHorizontal += $barWidth;
}
$html .= '</div>'.PHP_EOL;
return $html;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace AgeekDev\Barcode\Drivers;
use Imagick;
class JPG extends PNG
{
protected function createImagickImageObject(int $width, int $height): Imagick
{
$image = new Imagick;
$image->newImage($width, $height, 'white', 'JPG');
return $image;
}
protected function generateGdImage($image): void
{
imagejpeg($image);
imagedestroy($image);
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace AgeekDev\Barcode\Drivers;
use AgeekDev\Barcode\AbstractGenerator;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\BarcodeException;
use Imagick;
use imagickdraw;
use imagickpixel;
use Spatie\Color\Hex;
class PNG extends AbstractGenerator
{
protected bool $useImagick = true;
/**
* @throws BarcodeException
*/
public function __construct()
{
parent::__construct();
// Auto switch between GD and Imagick based on what is installed
if (extension_loaded('imagick')) {
$this->useImagick = true;
} elseif (function_exists('imagecreate')) {
$this->useImagick = false;
} else {
throw new BarcodeException('Neither gd-lib or imagick are installed!');
}
}
/**
* Force the use of Imagick image extension.
*/
public function useImagick(): self
{
$this->useImagick = true;
return $this;
}
/**
* Force the use of the GD image library.
*/
public function useGd(): self
{
$this->useImagick = false;
return $this;
}
/**
* @param string $text code to print
*
* @throws \ImagickDrawException
* @throws \ImagickException
* @throws \ImagickPixelException
*/
public function generate(string $text): string
{
$barcodeData = $this->getBarcodeData($text, $this->type);
$width = round($barcodeData->getWidth() * $this->widthFactor);
$foregroundColor = $this->getForegroundColor();
if ($this->useImagick) {
$imagickBarsShape = new imagickdraw;
$imagickBarsShape->setFillColor(new imagickpixel('rgb('.implode(',', $foregroundColor).')'));
} else {
$image = $this->createGdImageObject($width, $this->height);
$gdForegroundColor = imagecolorallocate($image, $foregroundColor[0], $foregroundColor[1], $foregroundColor[2]);
}
// print bars
$positionHorizontal = 0;
/** @var BarcodeBar $bar */
foreach ($barcodeData->getBars() as $bar) {
$barWidth = round(($bar->getWidth() * $this->widthFactor), 3);
if ($barWidth > 0 && $bar->isBar()) {
$y = round(($bar->getPositionVertical() * $this->height / $barcodeData->getHeight()), 3);
$barHeight = round(($bar->getHeight() * $this->height / $barcodeData->getHeight()), 3);
// draw a vertical bar
if ($this->useImagick) {
$imagickBarsShape->rectangle($positionHorizontal, $y, ($positionHorizontal + $barWidth - 1), ($y + $barHeight));
} else {
imagefilledrectangle($image, $positionHorizontal, $y, ($positionHorizontal + $barWidth - 1), ($y + $barHeight), $gdForegroundColor);
}
}
$positionHorizontal += $barWidth;
}
if ($this->useImagick) {
$image = $this->createImagickImageObject($width, $this->height);
$image->drawImage($imagickBarsShape);
return $image->getImageBlob();
}
ob_start();
$this->generateGdImage($image);
return ob_get_clean();
}
public function getForegroundColor(): string|array
{
$color = Hex::fromString($this->foregroundColor)->toRgba();
return [$color->red(), $color->blue(), $color->green()];
}
protected function createGdImageObject(int $width, int $height)
{
$image = imagecreate($width, $height);
$colorBackground = imagecolorallocate($image, 255, 255, 255);
imagecolortransparent($image, $colorBackground);
return $image;
}
/**
* @throws \ImagickException
*/
protected function createImagickImageObject(int $width, int $height): Imagick
{
$image = new Imagick;
$image->newImage($width, $height, 'none', 'PNG');
return $image;
}
protected function generateGdImage($image): void
{
imagepng($image);
imagedestroy($image);
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace AgeekDev\Barcode\Drivers;
use AgeekDev\Barcode\AbstractGenerator;
use AgeekDev\Barcode\BarcodeBar;
class SVG extends AbstractGenerator
{
/**
* Return SVG string representation of barcode.
*
* @param string $text code to print
*/
public function generate(string $text): string
{
$barcodeData = $this->getBarcodeData($text, $this->type);
// replace table for special characters
$repstr = ["\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;'];
$width = round(($barcodeData->getWidth() * $this->widthFactor), 3);
$svg = '<?xml version="1.0" standalone="no" ?>'.PHP_EOL;
$svg .= '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'.PHP_EOL;
$svg .= '<svg width="'.$width.'" height="'.$this->height.'" viewBox="0 0 '.$width.' '.$this->height.'" version="1.1" xmlns="http://www.w3.org/2000/svg">'.PHP_EOL;
$svg .= "\t".'<desc>'.strtr($barcodeData->getBarcode(), $repstr).'</desc>'.PHP_EOL;
$svg .= "\t".'<g id="bars" fill="'.$this->foregroundColor.'" stroke="none">'.PHP_EOL;
// print bars
$positionHorizontal = 0;
/** @var BarcodeBar $bar */
foreach ($barcodeData->getBars() as $bar) {
$barWidth = round(($bar->getWidth() * $this->widthFactor), 3);
$barHeight = round(($bar->getHeight() * $this->height / $barcodeData->getHeight()), 3);
if ($bar->isBar() && $barWidth > 0) {
$positionVertical = round(($bar->getPositionVertical() * $this->height / $barcodeData->getHeight()), 3);
// draw a vertical bar
$svg .= "\t\t".'<rect x="'.$positionHorizontal.'" y="'.$positionVertical.'" width="'.$barWidth.'" height="'.$barHeight.'" />'.PHP_EOL;
}
$positionHorizontal += $barWidth;
}
$svg .= "\t</g>".PHP_EOL;
$svg .= '</svg>'.PHP_EOL;
return $svg;
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace AgeekDev\Barcode\Enums;
use AgeekDev\Barcode\Types\TypeCodabar;
use AgeekDev\Barcode\Types\TypeCode11;
use AgeekDev\Barcode\Types\TypeCode128;
use AgeekDev\Barcode\Types\TypeCode128A;
use AgeekDev\Barcode\Types\TypeCode128B;
use AgeekDev\Barcode\Types\TypeCode128C;
use AgeekDev\Barcode\Types\TypeCode32;
use AgeekDev\Barcode\Types\TypeCode39;
use AgeekDev\Barcode\Types\TypeCode39Checksum;
use AgeekDev\Barcode\Types\TypeCode39Extended;
use AgeekDev\Barcode\Types\TypeCode39ExtendedChecksum;
use AgeekDev\Barcode\Types\TypeCode93;
use AgeekDev\Barcode\Types\TypeEan13;
use AgeekDev\Barcode\Types\TypeEan8;
use AgeekDev\Barcode\Types\TypeIntelligentMailBarcode;
use AgeekDev\Barcode\Types\TypeInterface;
use AgeekDev\Barcode\Types\TypeInterleaved25;
use AgeekDev\Barcode\Types\TypeInterleaved25Checksum;
use AgeekDev\Barcode\Types\TypeKix;
use AgeekDev\Barcode\Types\TypeMsi;
use AgeekDev\Barcode\Types\TypeMsiChecksum;
use AgeekDev\Barcode\Types\TypePharmacode;
use AgeekDev\Barcode\Types\TypePharmacodeTwoCode;
use AgeekDev\Barcode\Types\TypePlanet;
use AgeekDev\Barcode\Types\TypePostnet;
use AgeekDev\Barcode\Types\TypeRms4cc;
use AgeekDev\Barcode\Types\TypeStandard2of5;
use AgeekDev\Barcode\Types\TypeStandard2of5Checksum;
use AgeekDev\Barcode\Types\TypeUpcA;
use AgeekDev\Barcode\Types\TypeUpcE;
use AgeekDev\Barcode\Types\TypeUpcExtension2;
use AgeekDev\Barcode\Types\TypeUpcExtension5;
enum BarcodeType: string
{
case CODE_32 = 'C32';
case CODE_39 = 'C39';
case CODE_39_CHECKSUM = 'C39+';
case CODE_39E = 'C39E'; // CODE 39 EXTENDED
case CODE_39E_CHECKSUM = 'C39E+'; // CODE 39 EXTENDED + CHECKSUM
case CODE_93 = 'C93';
case STANDARD_2_5 = 'S25';
case STANDARD_2_5_CHECKSUM = 'S25+';
case INTERLEAVED_2_5 = 'I25';
case INTERLEAVED_2_5_CHECKSUM = 'I25+';
case CODE_128 = 'C128';
case CODE_128_A = 'C128A';
case CODE_128_B = 'C128B';
case CODE_128_C = 'C128C';
case EAN_2 = 'EAN2'; // 2-Digits UPC-Based Extention
case EAN_5 = 'EAN5'; // 5-Digits UPC-Based Extention
case EAN_8 = 'EAN8';
case EAN_13 = 'EAN13';
case UPC_A = 'UPCA';
case UPC_E = 'UPCE';
case MSI = 'MSI'; // MSI (Variation of Plessey code)
case MSI_CHECKSUM = 'MSI+'; // MSI + CHECKSUM (modulo 11)
case POSTNET = 'POSTNET';
case PLANET = 'PLANET';
case RMS4CC = 'RMS4CC'; // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
case KIX = 'KIX'; // KIX (Klant index - Customer index)
case IMB = 'IMB'; // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
case CODABAR = 'CODABAR';
case CODE_11 = 'CODE11';
case PHARMA_CODE = 'PHARMA';
case PHARMA_CODE_TWO_TRACKS = 'PHARMA2T';
public function class(): TypeInterface
{
return match ($this) {
self::CODE_32 => new TypeCode32,
self::CODE_39 => new TypeCode39,
self::CODE_39_CHECKSUM => new TypeCode39Checksum,
self::CODE_39E => new TypeCode39Extended,
self::CODE_39E_CHECKSUM => new TypeCode39ExtendedChecksum,
self::CODE_93 => new TypeCode93,
self::STANDARD_2_5 => new TypeStandard2of5,
self::STANDARD_2_5_CHECKSUM => new TypeStandard2of5Checksum,
self::INTERLEAVED_2_5 => new TypeInterleaved25,
self::INTERLEAVED_2_5_CHECKSUM => new TypeInterleaved25Checksum,
self::CODE_128 => new TypeCode128,
self::CODE_128_A => new TypeCode128A,
self::CODE_128_B => new TypeCode128B,
self::CODE_128_C => new TypeCode128C,
self::EAN_2 => new TypeUpcExtension2,
self::EAN_5 => new TypeUpcExtension5,
self::EAN_8 => new TypeEan8,
self::EAN_13 => new TypeEan13,
self::UPC_A => new TypeUpcA,
self::UPC_E => new TypeUpcE,
self::MSI => new TypeMsi,
self::MSI_CHECKSUM => new TypeMsiChecksum,
self::POSTNET => new TypePostnet,
self::PLANET => new TypePlanet,
self::RMS4CC => new TypeRms4cc,
self::KIX => new TypeKix,
self::IMB => new TypeIntelligentMailBarcode,
self::CODABAR => new TypeCodabar,
self::CODE_11 => new TypeCode11,
self::PHARMA_CODE => new TypePharmacode,
self::PHARMA_CODE_TWO_TRACKS => new TypePharmacodeTwoCode,
};
}
}

View File

@@ -0,0 +1,171 @@
<?php
namespace AgeekDev\Barcode\Enums;
use AgeekDev\Barcode\Types\TypeCodabar;
use AgeekDev\Barcode\Types\TypeCode11;
use AgeekDev\Barcode\Types\TypeCode128;
use AgeekDev\Barcode\Types\TypeCode128A;
use AgeekDev\Barcode\Types\TypeCode128B;
use AgeekDev\Barcode\Types\TypeCode128C;
use AgeekDev\Barcode\Types\TypeCode32;
use AgeekDev\Barcode\Types\TypeCode39;
use AgeekDev\Barcode\Types\TypeCode39Checksum;
use AgeekDev\Barcode\Types\TypeCode39Extended;
use AgeekDev\Barcode\Types\TypeCode39ExtendedChecksum;
use AgeekDev\Barcode\Types\TypeCode93;
use AgeekDev\Barcode\Types\TypeEan13;
use AgeekDev\Barcode\Types\TypeEan8;
use AgeekDev\Barcode\Types\TypeIntelligentMailBarcode;
use AgeekDev\Barcode\Types\TypeInterface;
use AgeekDev\Barcode\Types\TypeInterleaved25;
use AgeekDev\Barcode\Types\TypeInterleaved25Checksum;
use AgeekDev\Barcode\Types\TypeKix;
use AgeekDev\Barcode\Types\TypeMsi;
use AgeekDev\Barcode\Types\TypeMsiChecksum;
use AgeekDev\Barcode\Types\TypePharmacode;
use AgeekDev\Barcode\Types\TypePharmacodeTwoCode;
use AgeekDev\Barcode\Types\TypePlanet;
use AgeekDev\Barcode\Types\TypePostnet;
use AgeekDev\Barcode\Types\TypeRms4cc;
use AgeekDev\Barcode\Types\TypeStandard2of5;
use AgeekDev\Barcode\Types\TypeStandard2of5Checksum;
use AgeekDev\Barcode\Types\TypeUpcA;
use AgeekDev\Barcode\Types\TypeUpcE;
use AgeekDev\Barcode\Types\TypeUpcExtension2;
use AgeekDev\Barcode\Types\TypeUpcExtension5;
/**
* @deprecated see BarcodeType
*/
enum Type: string
{
case TYPE_CODE_32 = 'C32';
case TYPE_CODE_39 = 'C39';
case TYPE_CODE_39_CHECKSUM = 'C39+';
case TYPE_CODE_39E = 'C39E'; // CODE 39 EXTENDED
case TYPE_CODE_39E_CHECKSUM = 'C39E+'; // CODE 39 EXTENDED + CHECKSUM
case TYPE_CODE_93 = 'C93';
case TYPE_STANDARD_2_5 = 'S25';
case TYPE_STANDARD_2_5_CHECKSUM = 'S25+';
case TYPE_INTERLEAVED_2_5 = 'I25';
case TYPE_INTERLEAVED_2_5_CHECKSUM = 'I25+';
case TYPE_CODE_128 = 'C128';
case TYPE_CODE_128_A = 'C128A';
case TYPE_CODE_128_B = 'C128B';
case TYPE_CODE_128_C = 'C128C';
case TYPE_EAN_2 = 'EAN2'; // 2-Digits UPC-Based Extention
case TYPE_EAN_5 = 'EAN5'; // 5-Digits UPC-Based Extention
case TYPE_EAN_8 = 'EAN8';
case TYPE_EAN_13 = 'EAN13';
case TYPE_UPC_A = 'UPCA';
case TYPE_UPC_E = 'UPCE';
case TYPE_MSI = 'MSI'; // MSI (Variation of Plessey code)
case TYPE_MSI_CHECKSUM = 'MSI+'; // MSI + CHECKSUM (modulo 11)
case TYPE_POSTNET = 'POSTNET';
case TYPE_PLANET = 'PLANET';
case TYPE_RMS4CC = 'RMS4CC'; // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
case TYPE_KIX = 'KIX'; // KIX (Klant index - Customer index)
case TYPE_IMB = 'IMB'; // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
case TYPE_CODABAR = 'CODABAR';
case TYPE_CODE_11 = 'CODE11';
case TYPE_PHARMA_CODE = 'PHARMA';
case TYPE_PHARMA_CODE_TWO_TRACKS = 'PHARMA2T';
public function class(): TypeInterface
{
return match ($this) {
self::TYPE_CODE_32 => new TypeCode32,
self::TYPE_CODE_39 => new TypeCode39,
self::TYPE_CODE_39_CHECKSUM => new TypeCode39Checksum,
self::TYPE_CODE_39E => new TypeCode39Extended,
self::TYPE_CODE_39E_CHECKSUM => new TypeCode39ExtendedChecksum,
self::TYPE_CODE_93 => new TypeCode93,
self::TYPE_STANDARD_2_5 => new TypeStandard2of5,
self::TYPE_STANDARD_2_5_CHECKSUM => new TypeStandard2of5Checksum,
self::TYPE_INTERLEAVED_2_5 => new TypeInterleaved25,
self::TYPE_INTERLEAVED_2_5_CHECKSUM => new TypeInterleaved25Checksum,
self::TYPE_CODE_128 => new TypeCode128,
self::TYPE_CODE_128_A => new TypeCode128A,
self::TYPE_CODE_128_B => new TypeCode128B,
self::TYPE_CODE_128_C => new TypeCode128C,
self::TYPE_EAN_2 => new TypeUpcExtension2,
self::TYPE_EAN_5 => new TypeUpcExtension5,
self::TYPE_EAN_8 => new TypeEan8,
self::TYPE_EAN_13 => new TypeEan13,
self::TYPE_UPC_A => new TypeUpcA,
self::TYPE_UPC_E => new TypeUpcE,
self::TYPE_MSI => new TypeMsi,
self::TYPE_MSI_CHECKSUM => new TypeMsiChecksum,
self::TYPE_POSTNET => new TypePostnet,
self::TYPE_PLANET => new TypePlanet,
self::TYPE_RMS4CC => new TypeRms4cc,
self::TYPE_KIX => new TypeKix,
self::TYPE_IMB => new TypeIntelligentMailBarcode,
self::TYPE_CODABAR => new TypeCodabar,
self::TYPE_CODE_11 => new TypeCode11,
self::TYPE_PHARMA_CODE => new TypePharmacode,
self::TYPE_PHARMA_CODE_TWO_TRACKS => new TypePharmacodeTwoCode,
};
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace AgeekDev\Barcode\Exceptions;
class BarcodeException extends \Exception {}

View File

@@ -0,0 +1,5 @@
<?php
namespace AgeekDev\Barcode\Exceptions;
class InvalidCharacterException extends BarcodeException {}

View File

@@ -0,0 +1,5 @@
<?php
namespace AgeekDev\Barcode\Exceptions;
class InvalidCheckDigitException extends BarcodeException {}

View File

@@ -0,0 +1,5 @@
<?php
namespace AgeekDev\Barcode\Exceptions;
class InvalidFormatException extends BarcodeException {}

View File

@@ -0,0 +1,5 @@
<?php
namespace AgeekDev\Barcode\Exceptions;
class InvalidLengthException extends BarcodeException {}

View File

@@ -0,0 +1,5 @@
<?php
namespace AgeekDev\Barcode\Exceptions;
class UnknownTypeException extends BarcodeException {}

View File

@@ -0,0 +1,28 @@
<?php
namespace AgeekDev\Barcode\Facades;
use AgeekDev\Barcode\Contracts\ImageType;
use AgeekDev\Barcode\Enums\BarcodeType;
use AgeekDev\Barcode\Enums\Type;
use Illuminate\Support\Facades\Facade;
/**
* @method static ImageType foregroundColor(string $foregroundColor)
* @method static ImageType height(int $height)
* @method static ImageType widthFactor(int $widthFactor)
* @method static ImageType type(BarcodeType|Type $type)
* @method static ImageType imageType(?string $driver)
* @method static ImageType useImagick()
* @method static ImageType useGd()
* @method static string generate(string $text)
*
* @see \AgeekDev\Barcode\BarcodeManager
*/
class Barcode extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'laravel-barcode';
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace AgeekDev\Barcode\Helpers;
class BarcodeHelper
{
public static function getChecksum(string $code): int
{
$len = strlen($code);
$sum = 0;
for ($i = 0; $i < $len; $i += 2) {
$sum .= $code[$i];
}
$sum *= 3;
for ($i = 1; $i < $len; $i += 2) {
$sum .= ($code[$i]);
}
$r = $sum % 10;
if ($r > 0) {
$r = (10 - $r);
}
return $r;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace AgeekDev\Barcode\Helpers;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
/**
* Convert binary barcode sequence string to Barcode representation.
*/
class BinarySequenceConverter
{
public static function convert(string $code, string $sequence): Barcode
{
$barcode = new Barcode($code);
return self::generate($sequence, $barcode);
}
public static function generate(string $sequence, Barcode $barcode): Barcode
{
$len = strlen($sequence);
$barWidth = 0;
for ($i = 0; $i < $len; $i++) {
$barWidth++;
if (($i === ($len - 1)) || (($i < ($len - 1)) && ($sequence[$i] !== $sequence[($i + 1)]))) {
if ($sequence[$i] === '1') {
$drawBar = true;
} else {
$drawBar = false;
}
$barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar));
$barWidth = 0;
}
}
return $barcode;
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
/*
* CODABAR barcodes.
* Older code often used in library systems, sometimes in blood banks
*/
class TypeCodabar implements TypeInterface
{
protected array $conversionTable = [
'0' => '11111221',
'1' => '11112211',
'2' => '11121121',
'3' => '22111111',
'4' => '11211211',
'5' => '21111211',
'6' => '12111121',
'7' => '12112111',
'8' => '12211111',
'9' => '21121111',
'-' => '11122111',
'$' => '11221111',
':' => '21112121',
'/' => '21211121',
'.' => '21212111',
'+' => '11222221',
'A' => '11221211',
'B' => '12121121',
'C' => '11121221',
'D' => '11122211',
];
/**
* @throws InvalidCharacterException
*/
public function getBarcodeData(string $code): Barcode
{
$barcode = new Barcode($code);
$code = 'A'.strtoupper($code).'A';
for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) {
if (! isset($this->conversionTable[$code[$i]])) {
throw new InvalidCharacterException('Char '.$code[$i].' is unsupported');
}
$seq = $this->conversionTable[$code[$i]];
for ($j = 0; $j < 8; $j++) {
if (($j % 2) === 0) {
$drawBar = true;
} else {
$drawBar = false;
}
$barWidth = $seq[$j];
$barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar));
}
}
return $barcode;
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
/*
* CODE11 barcodes.
* Used primarily for labeling telecommunications equipment
*/
class TypeCode11 implements TypeInterface
{
protected array $conversionTable = [
'0' => '111121',
'1' => '211121',
'2' => '121121',
'3' => '221111',
'4' => '112121',
'5' => '212111',
'6' => '122111',
'7' => '111221',
'8' => '211211',
'9' => '211111',
'-' => '112111',
'S' => '112211',
];
/**
* @throws InvalidCharacterException
*/
public function getBarcodeData(string $code): Barcode
{
$barcode = new Barcode($code);
$code .= $this->getCheckDigitC($code);
$code .= $this->getCheckDigitK($code);
$code = 'S'.$code.'S';
for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) {
if (! isset($this->conversionTable[$code[$i]])) {
throw new InvalidCharacterException('Char '.$code[$i].' is unsupported');
}
$seq = $this->conversionTable[$code[$i]];
for ($j = 0, $jMax = strlen($seq); $j < $jMax; $j++) {
if (($j % 2) === 0) {
$drawBar = true;
} else {
$drawBar = false;
}
$barWidth = $seq[$j];
$barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar));
}
}
return $barcode;
}
private function getCheckDigitC(string $code): string
{
$p = 1;
$check = 0;
for ($i = (strlen($code) - 1); $i >= 0; $i--) {
$digit = $code[$i];
if ($digit === '-') {
$dval = 10;
} else {
$dval = (int) $digit;
}
$check += ($dval * $p);
$p++;
if ($p > 10) {
$p = 1;
}
}
$check %= 11;
if ($check === 10) {
$check = '-';
}
return $check;
}
private function getCheckDigitK(string $code): string
{
if (strlen($code) <= 10) {
return '';
}
$p = 1;
$check = 0;
for ($i = (strlen($code) - 1); $i >= 0; $i--) {
$digit = $code[$i];
if ($digit === '-') {
$dval = 10;
} else {
$dval = (int) $digit;
}
$check += ($dval * $p);
$p++;
if ($p > 9) {
$p = 1;
}
}
$check %= 11;
return (string) $check;
}
}

View File

@@ -0,0 +1,415 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
use AgeekDev\Barcode\Exceptions\InvalidLengthException;
/*
* C128 barcodes.
* Very capable code, excellent density, high reliability; in very wide use world-wide
*
* @param $code (string) code to represent.
* @param $type (string) barcode type: A, B, C or empty for automatic switch (AUTO mode)
*/
class TypeCode128 implements TypeInterface
{
protected ?string $type = null;
protected array $conversionTable = [
'212222', /* 00 */
'222122', /* 01 */
'222221', /* 02 */
'121223', /* 03 */
'121322', /* 04 */
'131222', /* 05 */
'122213', /* 06 */
'122312', /* 07 */
'132212', /* 08 */
'221213', /* 09 */
'221312', /* 10 */
'231212', /* 11 */
'112232', /* 12 */
'122132', /* 13 */
'122231', /* 14 */
'113222', /* 15 */
'123122', /* 16 */
'123221', /* 17 */
'223211', /* 18 */
'221132', /* 19 */
'221231', /* 20 */
'213212', /* 21 */
'223112', /* 22 */
'312131', /* 23 */
'311222', /* 24 */
'321122', /* 25 */
'321221', /* 26 */
'312212', /* 27 */
'322112', /* 28 */
'322211', /* 29 */
'212123', /* 30 */
'212321', /* 31 */
'232121', /* 32 */
'111323', /* 33 */
'131123', /* 34 */
'131321', /* 35 */
'112313', /* 36 */
'132113', /* 37 */
'132311', /* 38 */
'211313', /* 39 */
'231113', /* 40 */
'231311', /* 41 */
'112133', /* 42 */
'112331', /* 43 */
'132131', /* 44 */
'113123', /* 45 */
'113321', /* 46 */
'133121', /* 47 */
'313121', /* 48 */
'211331', /* 49 */
'231131', /* 50 */
'213113', /* 51 */
'213311', /* 52 */
'213131', /* 53 */
'311123', /* 54 */
'311321', /* 55 */
'331121', /* 56 */
'312113', /* 57 */
'312311', /* 58 */
'332111', /* 59 */
'314111', /* 60 */
'221411', /* 61 */
'431111', /* 62 */
'111224', /* 63 */
'111422', /* 64 */
'121124', /* 65 */
'121421', /* 66 */
'141122', /* 67 */
'141221', /* 68 */
'112214', /* 69 */
'112412', /* 70 */
'122114', /* 71 */
'122411', /* 72 */
'142112', /* 73 */
'142211', /* 74 */
'241211', /* 75 */
'221114', /* 76 */
'413111', /* 77 */
'241112', /* 78 */
'134111', /* 79 */
'111242', /* 80 */
'121142', /* 81 */
'121241', /* 82 */
'114212', /* 83 */
'124112', /* 84 */
'124211', /* 85 */
'411212', /* 86 */
'421112', /* 87 */
'421211', /* 88 */
'212141', /* 89 */
'214121', /* 90 */
'412121', /* 91 */
'111143', /* 92 */
'111341', /* 93 */
'131141', /* 94 */
'114113', /* 95 */
'114311', /* 96 */
'411113', /* 97 */
'411311', /* 98 */
'113141', /* 99 */
'114131', /* 100 */
'311141', /* 101 */
'411131', /* 102 */
'211412', /* 103 START A */
'211214', /* 104 START B */
'211232', /* 105 START C */
'233111', /* STOP */
'200000', /* END */
];
/**
* @throws InvalidCharacterException
* @throws InvalidLengthException
*/
public function getBarcodeData(string $code): Barcode
{
if (trim($code) === '') {
throw new InvalidLengthException('You should provide a barcode string.');
}
// ASCII characters for code A (ASCII 00 - 95)
$keys_a = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
$keys_a .= chr(0).chr(1).chr(2).chr(3).chr(4).chr(5).chr(6).chr(7).chr(8).chr(9);
$keys_a .= chr(10).chr(11).chr(12).chr(13).chr(14).chr(15).chr(16).chr(17).chr(18).chr(19);
$keys_a .= chr(20).chr(21).chr(22).chr(23).chr(24).chr(25).chr(26).chr(27).chr(28).chr(29);
$keys_a .= chr(30).chr(31);
// ASCII characters for code B (ASCII 32 - 127)
$keys_b = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.chr(127);
// special codes
$fnc_a = [241 => 102, 242 => 97, 243 => 96, 244 => 101];
$fnc_b = [241 => 102, 242 => 97, 243 => 96, 244 => 100];
// array of symbols
$code_data = [];
// length of the code
$len = strlen($code);
switch (strtoupper($this->type ?? '')) {
case 'A':
$startid = 103;
for ($i = 0; $i < $len; $i++) {
$char = $code[$i];
$char_id = ord($char);
if (($char_id <= 244) && ($char_id >= 241)) {
$code_data[] = $fnc_a[$char_id];
} elseif ($char_id <= 95) {
$code_data[] = strpos($keys_a, $char);
} else {
throw new InvalidCharacterException('Char '.$char.' is unsupported');
}
}
break;
case 'B':
$startid = 104;
for ($i = 0; $i < $len; $i++) {
$char = $code[$i];
$char_id = ord($char);
if (($char_id >= 241) && ($char_id <= 244)) {
$code_data[] = $fnc_b[$char_id];
} elseif (($char_id >= 32) && ($char_id <= 127)) {
$code_data[] = strpos($keys_b, $char);
} else {
throw new InvalidCharacterException('Char '.$char.' is unsupported');
}
}
break;
case 'C':
$startid = 105;
if (ord($code[0]) === 241) {
$code_data[] = 102;
$code = substr($code, 1);
$len--;
}
if (($len % 2) !== 0) {
throw new InvalidLengthException('Length must be even');
}
for ($i = 0; $i < $len; $i += 2) {
$chrnum = $code[$i].$code[$i + 1];
if (preg_match('/(\d{2})/', $chrnum) > 0) {
$code_data[] = (int) $chrnum;
} else {
throw new InvalidCharacterException;
}
}
break;
default:
// split code into sequences
$sequence = [];
// get numeric sequences (if any)
$numseq = [];
preg_match_all('/(\d{4,})/', $code, $numseq, PREG_OFFSET_CAPTURE);
if (! empty($numseq[1])) {
$end_offset = 0;
foreach ($numseq[1] as $val) {
$offset = $val[1];
// numeric sequence
$slen = strlen($val[0]);
if (($slen % 2) != 0) {
// the length must be even
$offset++;
$val[0] = substr($val[0], 1);
}
if ($offset > $end_offset) {
// non numeric sequence
$sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset, ($offset - $end_offset))));
}
// numeric sequence fallback
$slen = strlen($val[0]);
if (($slen % 2) != 0) {
// the length must be even
$slen--;
}
$sequence[] = ['C', substr($code, $offset, $slen), $slen];
$end_offset = $offset + $slen;
}
if ($end_offset < $len) {
$sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset)));
}
} else {
// text code (non C mode)
$sequence = array_merge($sequence, $this->get128ABsequence($code));
}
$startid = 0;
// process the sequence
foreach ($sequence as $key => $seq) {
switch ($seq[0]) {
case 'A':
if ($key === 0) {
$startid = 103;
} elseif ($sequence[($key - 1)][0] !== 'A') {
if (($seq[2] === 1) && ($key > 0) && ($sequence[($key - 1)][0] === 'B') && (! isset($sequence[($key - 1)][3]))) {
// single character shift
$code_data[] = 98;
// mark shift
$sequence[$key][3] = true;
} elseif (! isset($sequence[($key - 1)][3])) {
$code_data[] = 101;
}
}
for ($i = 0; $i < $seq[2]; $i++) {
$char = $seq[1][$i];
$char_id = ord($char);
if (($char_id >= 241) && ($char_id <= 244)) {
$code_data[] = $fnc_a[$char_id];
} else {
$code_data[] = strpos($keys_a, $char);
}
}
break;
case 'B':
if ($key === 0) {
$tmpchr = ord($seq[1][0]);
if (($seq[2] === 1) && ($tmpchr >= 241) && ($tmpchr <= 244) && isset($sequence[($key + 1)]) && ($sequence[($key + 1)][0] !== 'B')) {
switch ($sequence[($key + 1)][0]) {
case 'A':
$startid = 103;
$sequence[$key][0] = 'A';
$code_data[] = $fnc_a[$tmpchr];
break;
case 'C':
$startid = 105;
$sequence[$key][0] = 'C';
$code_data[] = $fnc_a[$tmpchr];
break;
}
break;
}
$startid = 104;
} elseif ($sequence[($key - 1)][0] !== 'B') {
if (($seq[2] === 1) && ($key > 0) && ($sequence[($key - 1)][0] === 'A') && (! isset($sequence[($key - 1)][3]))) {
// single character shift
$code_data[] = 98;
// mark shift
$sequence[$key][3] = true;
} elseif (! isset($sequence[($key - 1)][3])) {
$code_data[] = 100;
}
}
for ($i = 0; $i < $seq[2]; $i++) {
$char = $seq[1][$i];
$char_id = ord($char);
if (($char_id >= 241) && ($char_id <= 244)) {
$code_data[] = $fnc_b[$char_id];
} else {
$code_data[] = strpos($keys_b, $char);
}
}
break;
case 'C':
if ($key === 0) {
$startid = 105;
} elseif ($sequence[($key - 1)][0] !== 'C') {
$code_data[] = 99;
}
for ($i = 0; $i < $seq[2]; $i += 2) {
$chrnum = $seq[1][$i].$seq[1][$i + 1];
$code_data[] = (int) $chrnum;
}
break;
}
}
}
// calculate check character
$sum = $startid;
foreach ($code_data as $key => $val) {
$sum += ($val * ($key + 1));
}
// add check character
$code_data[] = ($sum % 103);
// add stop sequence
$code_data[] = 106;
$code_data[] = 107;
// add start code at the beginning
array_unshift($code_data, $startid);
// build barcode array
$barcode = new Barcode($code);
foreach ($code_data as $val) {
$seq = $this->conversionTable[$val];
for ($j = 0; $j < 6; $j++) {
if (($j % 2) === 0) {
$t = true; // bar
} else {
$t = false; // space
}
$w = $seq[$j];
$barcode->addBar(new BarcodeBar($w, 1, $t));
}
}
return $barcode;
}
/**
* Split text code in A/B sequence for 128 code
*
* @param $code (string) code to split.
*/
protected function get128ABsequence(string $code): array
{
$len = strlen($code);
$sequence = [];
// get A sequences (if any)
$numseq = [];
preg_match_all('/([\x00-\x1f])/', $code, $numseq, PREG_OFFSET_CAPTURE);
if (! empty($numseq[1])) {
$end_offset = 0;
foreach ($numseq[1] as $val) {
$offset = $val[1];
if ($offset > $end_offset) {
// B sequence
$sequence[] = [
'B',
substr($code, $end_offset, ($offset - $end_offset)),
($offset - $end_offset),
];
}
// A sequence
$slen = strlen($val[0]);
$sequence[] = ['A', substr($code, $offset, $slen), $slen];
$end_offset = $offset + $slen;
}
if ($end_offset < $len) {
$sequence[] = ['B', substr($code, $end_offset), ($len - $end_offset)];
}
} else {
// only B sequence
$sequence[] = ['B', $code, $len];
}
return $sequence;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* C128 barcodes.
* Very capable code, excellent density, high reliability; in very wide use world-wide
*
* @param $code (string) code to represent.
* @param $type (string) barcode type: A, B, C or empty for automatic switch (AUTO mode)
*/
class TypeCode128A extends TypeCode128
{
protected ?string $type = 'A';
}

View File

@@ -0,0 +1,16 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* C128 barcodes.
* Very capable code, excellent density, high reliability; in very wide use world-wide
*
* @param $code (string) code to represent.
* @param $type (string) barcode type: A, B, C or empty for automatic switch (AUTO mode)
*/
class TypeCode128B extends TypeCode128
{
protected ?string $type = 'B';
}

View File

@@ -0,0 +1,16 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* C128 barcodes.
* Very capable code, excellent density, high reliability; in very wide use world-wide
*
* @param $code (string) code to represent.
* @param $type (string) barcode type: A, B, C or empty for automatic switch (AUTO mode)
*/
class TypeCode128C extends TypeCode128
{
protected ?string $type = 'C';
}

View File

@@ -0,0 +1,60 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
/*
* CODE 32 - italian pharmaceutical
* General-purpose code in very wide use world-wide
*/
class TypeCode32 extends TypeCode39
{
protected array $conversionTable32 = [
'0' => '0',
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4',
'5' => '5',
'6' => '6',
'7' => '7',
'8' => '8',
'9' => '9',
'10' => 'B',
'11' => 'C',
'12' => 'D',
'13' => 'F',
'14' => 'G',
'15' => 'H',
'16' => 'J',
'17' => 'K',
'18' => 'L',
'19' => 'M',
'20' => 'N',
'21' => 'P',
'22' => 'Q',
'23' => 'R',
'24' => 'S',
'25' => 'T',
'26' => 'U',
'27' => 'V',
'28' => 'W',
'29' => 'X',
'30' => 'Y',
'31' => 'Z',
];
public function getBarcodeData(string $code): Barcode
{
$code39 = '';
$codeElab = $code;
for ($e = 5; $e >= 0; $e--) {
$code39 .= $this->conversionTable32[(int) ($codeElab / (32 ** $e))];
$codeElab %= 32 ** $e;
}
return parent::getBarcodeData($code39);
}
}

View File

@@ -0,0 +1,330 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
use AgeekDev\Barcode\Exceptions\InvalidLengthException;
/*
* CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
* General-purpose code in very wide use world-wide
*/
class TypeCode39 implements TypeInterface
{
protected bool $extended = false;
protected bool $checksum = false;
protected array $conversionTable = [
'0' => '111331311',
'1' => '311311113',
'2' => '113311113',
'3' => '313311111',
'4' => '111331113',
'5' => '311331111',
'6' => '113331111',
'7' => '111311313',
'8' => '311311311',
'9' => '113311311',
'A' => '311113113',
'B' => '113113113',
'C' => '313113111',
'D' => '111133113',
'E' => '311133111',
'F' => '113133111',
'G' => '111113313',
'H' => '311113311',
'I' => '113113311',
'J' => '111133311',
'K' => '311111133',
'L' => '113111133',
'M' => '313111131',
'N' => '111131133',
'O' => '311131131',
'P' => '113131131',
'Q' => '111111333',
'R' => '311111331',
'S' => '113111331',
'T' => '111131331',
'U' => '331111113',
'V' => '133111113',
'W' => '333111111',
'X' => '131131113',
'Y' => '331131111',
'Z' => '133131111',
'-' => '131111313',
'.' => '331111311',
' ' => '133111311',
'$' => '131313111',
'/' => '131311131',
'+' => '131113131',
'%' => '111313131',
'*' => '131131311',
];
/**
* @throws InvalidCharacterException
* @throws InvalidLengthException
*/
public function getBarcodeData(string $code): Barcode
{
if (trim($code) === '') {
throw new InvalidLengthException('You should provide a barcode string.');
}
if ($this->extended) {
// extended mode
$code = $this->encode_code39_ext($code);
}
if ($this->checksum) {
// checksum
$code .= $this->checksum_code39($code);
}
// add start and stop codes
$code = '*'.$code.'*';
$barcode = new Barcode($code);
for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) {
$char = $code[$i];
if (! isset($this->conversionTable[$char])) {
throw new InvalidCharacterException('Char '.$char.' is unsupported');
}
for ($j = 0; $j < 9; $j++) {
if (($j % 2) === 0) {
$drawBar = true;
} else {
$drawBar = false;
}
$barWidth = $this->conversionTable[$char][$j];
$barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar));
}
// inter character gap
$barcode->addBar(new BarcodeBar(1, 1, false));
}
return $barcode;
}
/**
* Encode a string to be used for CODE 39 Extended mode.
*
* @param string $code code to represent.
*
* @throws InvalidCharacterException
*/
protected function encode_code39_ext(string $code): string
{
$encode = [
chr(0) => '%U',
chr(1) => '$A',
chr(2) => '$B',
chr(3) => '$C',
chr(4) => '$D',
chr(5) => '$E',
chr(6) => '$F',
chr(7) => '$G',
chr(8) => '$H',
chr(9) => '$I',
chr(10) => '$J',
chr(11) => '$K',
chr(12) => '$L',
chr(13) => '$M',
chr(14) => '$N',
chr(15) => '$O',
chr(16) => '$P',
chr(17) => '$Q',
chr(18) => '$R',
chr(19) => '$S',
chr(20) => '$T',
chr(21) => '$U',
chr(22) => '$V',
chr(23) => '$W',
chr(24) => '$X',
chr(25) => '$Y',
chr(26) => '$Z',
chr(27) => '%A',
chr(28) => '%B',
chr(29) => '%C',
chr(30) => '%D',
chr(31) => '%E',
chr(32) => ' ',
chr(33) => '/A',
chr(34) => '/B',
chr(35) => '/C',
chr(36) => '/D',
chr(37) => '/E',
chr(38) => '/F',
chr(39) => '/G',
chr(40) => '/H',
chr(41) => '/I',
chr(42) => '/J',
chr(43) => '/K',
chr(44) => '/L',
chr(45) => '-',
chr(46) => '.',
chr(47) => '/O',
chr(48) => '0',
chr(49) => '1',
chr(50) => '2',
chr(51) => '3',
chr(52) => '4',
chr(53) => '5',
chr(54) => '6',
chr(55) => '7',
chr(56) => '8',
chr(57) => '9',
chr(58) => '/Z',
chr(59) => '%F',
chr(60) => '%G',
chr(61) => '%H',
chr(62) => '%I',
chr(63) => '%J',
chr(64) => '%V',
chr(65) => 'A',
chr(66) => 'B',
chr(67) => 'C',
chr(68) => 'D',
chr(69) => 'E',
chr(70) => 'F',
chr(71) => 'G',
chr(72) => 'H',
chr(73) => 'I',
chr(74) => 'J',
chr(75) => 'K',
chr(76) => 'L',
chr(77) => 'M',
chr(78) => 'N',
chr(79) => 'O',
chr(80) => 'P',
chr(81) => 'Q',
chr(82) => 'R',
chr(83) => 'S',
chr(84) => 'T',
chr(85) => 'U',
chr(86) => 'V',
chr(87) => 'W',
chr(88) => 'X',
chr(89) => 'Y',
chr(90) => 'Z',
chr(91) => '%K',
chr(92) => '%L',
chr(93) => '%M',
chr(94) => '%N',
chr(95) => '%O',
chr(96) => '%W',
chr(97) => '+A',
chr(98) => '+B',
chr(99) => '+C',
chr(100) => '+D',
chr(101) => '+E',
chr(102) => '+F',
chr(103) => '+G',
chr(104) => '+H',
chr(105) => '+I',
chr(106) => '+J',
chr(107) => '+K',
chr(108) => '+L',
chr(109) => '+M',
chr(110) => '+N',
chr(111) => '+O',
chr(112) => '+P',
chr(113) => '+Q',
chr(114) => '+R',
chr(115) => '+S',
chr(116) => '+T',
chr(117) => '+U',
chr(118) => '+V',
chr(119) => '+W',
chr(120) => '+X',
chr(121) => '+Y',
chr(122) => '+Z',
chr(123) => '%P',
chr(124) => '%Q',
chr(125) => '%R',
chr(126) => '%S',
chr(127) => '%T',
];
$code_ext = '';
for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) {
if (ord($code[$i]) > 127) {
throw new InvalidCharacterException('Only supports till char 127');
}
$code_ext .= $encode[$code[$i]];
}
return $code_ext;
}
/**
* Calculate CODE 39 checksum (modulo 43).
*
* @param string $code code to represent.
*/
protected function checksum_code39(string $code): string
{
$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',
'-',
'.',
' ',
'$',
'/',
'+',
'%',
];
$sum = 0;
for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) {
$k = array_keys($chars, $code[$i]);
$sum += $k[0];
}
$j = ($sum % 43);
return $chars[$j];
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
* General-purpose code in very wide use world-wide
*/
class TypeCode39Checksum extends TypeCode39
{
protected bool $extended = false;
protected bool $checksum = true;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
* General-purpose code in very wide use world-wide
*/
class TypeCode39Extended extends TypeCode39
{
protected bool $extended = true;
protected bool $checksum = false;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
* General-purpose code in very wide use world-wide
*/
class TypeCode39ExtendedChecksum extends TypeCode39
{
protected bool $extended = true;
protected bool $checksum = true;
}

View File

@@ -0,0 +1,286 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
/*
* CODE 93 - USS-93
* Compact code similar to Code 39
*
* reference: https://en.wikipedia.org/wiki/Code_93#Full_ASCII_Code_93
*/
class TypeCode93 implements TypeInterface
{
protected array $conversionTable = [
48 => '131112', // 0
49 => '111213', // 1
50 => '111312', // 2
51 => '111411', // 3
52 => '121113', // 4
53 => '121212', // 5
54 => '121311', // 6
55 => '111114', // 7
56 => '131211', // 8
57 => '141111', // 9
65 => '211113', // A
66 => '211212', // B
67 => '211311', // C
68 => '221112', // D
69 => '221211', // E
70 => '231111', // F
71 => '112113', // G
72 => '112212', // H
73 => '112311', // I
74 => '122112', // J
75 => '132111', // K
76 => '111123', // L
77 => '111222', // M
78 => '111321', // N
79 => '121122', // O
80 => '131121', // P
81 => '212112', // Q
82 => '212211', // R
83 => '211122', // S
84 => '211221', // T
85 => '221121', // U
86 => '222111', // V
87 => '112122', // W
88 => '112221', // X
89 => '122121', // Y
90 => '123111', // Z
45 => '121131', // -
46 => '311112', // .
32 => '311211', //
36 => '321111', // $
47 => '112131', // /
43 => '113121', // +
37 => '211131', // %
97 => '121221', // ($)
98 => '312111', // (%)
99 => '311121', // (/)
100 => '122211', // (+)
42 => '111141', // start-stop
];
/**
* @throws InvalidCharacterException
*/
public function getBarcodeData(string $code): Barcode
{
$encode = [
chr(0) => 'bU',
chr(1) => 'aA',
chr(2) => 'aB',
chr(3) => 'aC',
chr(4) => 'aD',
chr(5) => 'aE',
chr(6) => 'aF',
chr(7) => 'aG',
chr(8) => 'aH',
chr(9) => 'aI',
chr(10) => 'aJ',
chr(11) => 'aK',
chr(12) => 'aL',
chr(13) => 'aM',
chr(14) => 'aN',
chr(15) => 'aO',
chr(16) => 'aP',
chr(17) => 'aQ',
chr(18) => 'aR',
chr(19) => 'aS',
chr(20) => 'aT',
chr(21) => 'aU',
chr(22) => 'aV',
chr(23) => 'aW',
chr(24) => 'aX',
chr(25) => 'aY',
chr(26) => 'aZ',
chr(27) => 'bA',
chr(28) => 'bB',
chr(29) => 'bC',
chr(30) => 'bD',
chr(31) => 'bE',
chr(32) => ' ',
chr(33) => 'cA',
chr(34) => 'cB',
chr(35) => 'cC',
chr(36) => '$',
chr(37) => '%',
chr(38) => 'cF',
chr(39) => 'cG',
chr(40) => 'cH',
chr(41) => 'cI',
chr(42) => 'cJ',
chr(43) => '+',
chr(44) => 'cL',
chr(45) => '-',
chr(46) => '.',
chr(47) => '/',
chr(48) => '0',
chr(49) => '1',
chr(50) => '2',
chr(51) => '3',
chr(52) => '4',
chr(53) => '5',
chr(54) => '6',
chr(55) => '7',
chr(56) => '8',
chr(57) => '9',
chr(58) => 'cZ',
chr(59) => 'bF',
chr(60) => 'bG',
chr(61) => 'bH',
chr(62) => 'bI',
chr(63) => 'bJ',
chr(64) => 'bV',
chr(65) => 'A',
chr(66) => 'B',
chr(67) => 'C',
chr(68) => 'D',
chr(69) => 'E',
chr(70) => 'F',
chr(71) => 'G',
chr(72) => 'H',
chr(73) => 'I',
chr(74) => 'J',
chr(75) => 'K',
chr(76) => 'L',
chr(77) => 'M',
chr(78) => 'N',
chr(79) => 'O',
chr(80) => 'P',
chr(81) => 'Q',
chr(82) => 'R',
chr(83) => 'S',
chr(84) => 'T',
chr(85) => 'U',
chr(86) => 'V',
chr(87) => 'W',
chr(88) => 'X',
chr(89) => 'Y',
chr(90) => 'Z',
chr(91) => 'bK',
chr(92) => 'bL',
chr(93) => 'bM',
chr(94) => 'bN',
chr(95) => 'bO',
chr(96) => 'bW',
chr(97) => 'dA',
chr(98) => 'dB',
chr(99) => 'dC',
chr(100) => 'dD',
chr(101) => 'dE',
chr(102) => 'dF',
chr(103) => 'dG',
chr(104) => 'dH',
chr(105) => 'dI',
chr(106) => 'dJ',
chr(107) => 'dK',
chr(108) => 'dL',
chr(109) => 'dM',
chr(110) => 'dN',
chr(111) => 'dO',
chr(112) => 'dP',
chr(113) => 'dQ',
chr(114) => 'dR',
chr(115) => 'dS',
chr(116) => 'dT',
chr(117) => 'dU',
chr(118) => 'dV',
chr(119) => 'dW',
chr(120) => 'dX',
chr(121) => 'dY',
chr(122) => 'dZ',
chr(123) => 'bP',
chr(124) => 'bQ',
chr(125) => 'bR',
chr(126) => 'bS',
chr(127) => 'bT',
];
$code_ext = '';
$clen = strlen($code);
for ($i = 0; $i < $clen; $i++) {
if (ord($code[$i]) > 127) {
throw new InvalidCharacterException('Only supports till char 127');
}
$code_ext .= $encode[$code[$i]];
}
// checksum
$code_ext .= $this->checksum_code93($code_ext);
// add start and stop codes
$code = '*'.$code_ext.'*';
$barcode = new Barcode($code);
for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) {
$char = ord($code[$i]);
if (! isset($this->conversionTable[$char])) {
throw new InvalidCharacterException('Char '.$char.' is unsupported');
}
for ($j = 0; $j < 6; $j++) {
if (($j % 2) === 0) {
$drawBar = true;
} else {
$drawBar = false;
}
$barWidth = $this->conversionTable[$char][$j];
$barcode->addBar(new BarcodeBar($barWidth, 1, $drawBar));
}
}
$barcode->addBar(new BarcodeBar(1, 1, true));
return $barcode;
}
/**
* Calculate CODE 93 checksum (modulo 47).
*
* @param $code (string) code to represent.
*/
protected function checksum_code93(string $code): string
{
$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'];
// calculate check digit C
$len = strlen($code);
$p = 1;
$check = 0;
for ($i = ($len - 1); $i >= 0; $i--) {
$k = array_keys($chars, $code[$i]);
$check += ($k[0] * $p);
$p++;
if ($p > 20) {
$p = 1;
}
}
$check %= 47;
$c = $chars[$check];
$code .= $c;
// calculate check digit K
$p = 1;
$check = 0;
for ($i = $len; $i >= 0; $i--) {
$k = array_keys($chars, $code[$i]);
$check += ($k[0] * $p);
$p++;
if ($p > 15) {
$p = 1;
}
}
$check %= 47;
$k = $chars[$check];
return $c.$k;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* EAN13 and UPC-A barcodes.
* EAN13: European Article Numbering international retail product code
* UPC-A: Universal product code seen on almost all retail products in the USA and Canada
* UPC-E: Short version of UPC symbol
*
* @param $code (string) code to represent.
* @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
*/
class TypeEan13 extends TypeEanUpcBase
{
protected int $length = 13;
protected bool $upca = false;
protected bool $upce = false;
}

View File

@@ -0,0 +1,22 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* EAN13 and UPC-A barcodes.
* EAN13: European Article Numbering international retail product code
* UPC-A: Universal product code seen on almost all retail products in the USA and Canada
* UPC-E: Short version of UPC symbol
*
* @param $code (string) code to represent.
* @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
*/
class TypeEan8 extends TypeEanUpcBase
{
protected int $length = 8;
protected bool $upca = false;
protected bool $upce = false;
}

View File

@@ -0,0 +1,224 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
use AgeekDev\Barcode\Exceptions\InvalidCheckDigitException;
use AgeekDev\Barcode\Exceptions\InvalidLengthException;
use AgeekDev\Barcode\Helpers\BinarySequenceConverter;
/*
* EAN13 and UPC-A barcodes.
* EAN13: European Article Numbering international retail product code
* UPC-A: Universal product code seen on almost all retail products in the USA and Canada
* UPC-E: Short version of UPC symbol
*
* @param $code (string) code to represent.
* @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
*/
abstract class TypeEanUpcBase implements TypeInterface
{
protected int $length = 13;
protected bool $upca = false;
protected bool $upce = false;
/**
* @throws InvalidCheckDigitException
* @throws InvalidCharacterException
* @throws InvalidLengthException
*/
public function getBarcodeData(string $code): Barcode
{
if (trim($code) === '') {
throw new InvalidLengthException('You should provide a barcode string.');
}
$length = $this->length;
$dataLength = $length - 1;
// Add zero padding in front
$code = str_pad($code, $dataLength, '0', STR_PAD_LEFT);
$checksumDigit = $this->calculateChecksumDigit($code);
if (strlen($code) === $dataLength) {
$code .= $checksumDigit;
} elseif ($checksumDigit !== (int) $code[$dataLength]) {
// If length of given barcode is same as final length, barcode is including checksum
// Make sure that checksum is the same as we calculated
throw new InvalidCheckDigitException;
}
if ($this->upca || $this->upce) {
$code = '0'.$code;
$length++;
}
if ($this->upce) {
// convert UPC-A to UPC-E
$tmp = substr($code, 4, 3);
if (($tmp === '000') || ($tmp === '100') || ($tmp === '200')) {
// manufacturer code ends in 000, 100, or 200
$upce_code = substr($code, 2, 2).substr($code, 9, 3).$code[4];
} else {
$tmp = substr($code, 5, 2);
if ($tmp === '00') {
// manufacturer code ends in 00
$upce_code = substr($code, 2, 3).substr($code, 10, 2).'3';
} else {
$tmp = $code[6];
if ($tmp === '0') {
// manufacturer code ends in 0
$upce_code = substr($code, 2, 4).$code[11].'4';
} else {
// manufacturer code does not end in zero
$upce_code = substr($code, 2, 5).$code[11];
}
}
}
}
// Convert digits to bars
$codes = [
'A' => [ // left odd parity
'0' => '0001101',
'1' => '0011001',
'2' => '0010011',
'3' => '0111101',
'4' => '0100011',
'5' => '0110001',
'6' => '0101111',
'7' => '0111011',
'8' => '0110111',
'9' => '0001011',
],
'B' => [ // left even parity
'0' => '0100111',
'1' => '0110011',
'2' => '0011011',
'3' => '0100001',
'4' => '0011101',
'5' => '0111001',
'6' => '0000101',
'7' => '0010001',
'8' => '0001001',
'9' => '0010111',
],
'C' => [ // right
'0' => '1110010',
'1' => '1100110',
'2' => '1101100',
'3' => '1000010',
'4' => '1011100',
'5' => '1001110',
'6' => '1010000',
'7' => '1000100',
'8' => '1001000',
'9' => '1110100',
],
];
$parities = [
'0' => ['A', 'A', 'A', 'A', 'A', 'A'],
'1' => ['A', 'A', 'B', 'A', 'B', 'B'],
'2' => ['A', 'A', 'B', 'B', 'A', 'B'],
'3' => ['A', 'A', 'B', 'B', 'B', 'A'],
'4' => ['A', 'B', 'A', 'A', 'B', 'B'],
'5' => ['A', 'B', 'B', 'A', 'A', 'B'],
'6' => ['A', 'B', 'B', 'B', 'A', 'A'],
'7' => ['A', 'B', 'A', 'B', 'A', 'B'],
'8' => ['A', 'B', 'A', 'B', 'B', 'A'],
'9' => ['A', 'B', 'B', 'A', 'B', 'A'],
];
$upce_parities = [
[
'0' => ['B', 'B', 'B', 'A', 'A', 'A'],
'1' => ['B', 'B', 'A', 'B', 'A', 'A'],
'2' => ['B', 'B', 'A', 'A', 'B', 'A'],
'3' => ['B', 'B', 'A', 'A', 'A', 'B'],
'4' => ['B', 'A', 'B', 'B', 'A', 'A'],
'5' => ['B', 'A', 'A', 'B', 'B', 'A'],
'6' => ['B', 'A', 'A', 'A', 'B', 'B'],
'7' => ['B', 'A', 'B', 'A', 'B', 'A'],
'8' => ['B', 'A', 'B', 'A', 'A', 'B'],
'9' => ['B', 'A', 'A', 'B', 'A', 'B'],
],
[
'0' => ['A', 'A', 'A', 'B', 'B', 'B'],
'1' => ['A', 'A', 'B', 'A', 'B', 'B'],
'2' => ['A', 'A', 'B', 'B', 'A', 'B'],
'3' => ['A', 'A', 'B', 'B', 'B', 'A'],
'4' => ['A', 'B', 'A', 'A', 'B', 'B'],
'5' => ['A', 'B', 'B', 'A', 'A', 'B'],
'6' => ['A', 'B', 'B', 'B', 'A', 'A'],
'7' => ['A', 'B', 'A', 'B', 'A', 'B'],
'8' => ['A', 'B', 'A', 'B', 'B', 'A'],
'9' => ['A', 'B', 'B', 'A', 'B', 'A'],
],
];
$seq = '101'; // left guard bar
if ($this->upce) {
$barcode = new Barcode($upce_code);
$p = $upce_parities[$code[1]][$checksumDigit];
for ($i = 0; $i < 6; $i++) {
$seq .= $codes[$p[$i]][$upce_code[$i]];
}
$seq .= '010101'; // right guard bar
} else {
$barcode = new Barcode($code);
$half_len = (int) ceil($length / 2);
if ($length === 8) {
for ($i = 0; $i < $half_len; $i++) {
$seq .= $codes['A'][$code[$i]];
}
} else {
$p = $parities[$code[0]];
for ($i = 1; $i < $half_len; $i++) {
$seq .= $codes[$p[$i - 1]][$code[$i]];
}
}
$seq .= '01010'; // center guard bar
for ($i = $half_len; $i < $length; $i++) {
if (! isset($codes['C'][$code[$i]])) {
throw new InvalidCharacterException('Char '.$code[$i].' not allowed');
}
$seq .= $codes['C'][$code[$i]];
}
$seq .= '101'; // right guard bar
}
return BinarySequenceConverter::generate($seq, $barcode);
}
protected function calculateChecksumDigit(string $code): int
{
// calculate check digit
$sum_a = 0;
for ($i = 1; $i < $this->length - 1; $i += 2) {
$sum_a += (int) $code[$i];
}
if ($this->length > 12) {
$sum_a *= 3;
}
$sum_b = 0;
for ($i = 0; $i < $this->length - 1; $i += 2) {
$sum_b += (int) $code[$i];
}
if ($this->length < 13) {
$sum_b *= 3;
}
$checksumDigit = ($sum_a + $sum_b) % 10;
if ($checksumDigit > 0) {
$checksumDigit = (10 - $checksumDigit);
}
return $checksumDigit;
}
}

View File

@@ -0,0 +1,549 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\BarcodeException;
/*
* IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
* (requires PHP bcmath extension)
* Intelligent Mail barcode is a 65-bar code for use on mail in the United States.
* The fields are described as follows:<ul><li>The Barcode Identifier shall be assigned by USPS to encode the
* presort identification that is currently printed in human-readable form on the optional endorsement line (OEL)
* as well as for future USPS use. This shall be two digits, with the second digit in the range of 04. The
* allowable encoding ranges shall be 0004, 1014, 2024, 3034, 4044, 5054, 6064, 7074, 8084, and
* 9094.</li><li>The Service Type Identifier shall be assigned by USPS for any combination of services requested
* on the mailpiece. The allowable encoding range shall be 000http://it2.php.net/manual/en/function.dechex.php999.
* Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each
* service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier
* values.</li><li>The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digits number
* that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000-
* 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.</li><li>The
* Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The
* allowable encoding range shall be 000000000999999999 when used with a 6 digit Mailer ID and 000000-999999 when
* used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the
* mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be
* 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 0000099999, 000000000999999999,
* and 0000000000099999999999.</li></ul>
*
* code to print, separate the ZIP (routing code) from the rest using a minus char '-'
* (BarcodeID_ServiceTypeID_MailerID_SerialNumber-RoutingCode)
*/
class TypeIntelligentMailBarcode implements TypeInterface
{
/**
* @throws BarcodeException
*/
public function getBarcodeData(string $code): Barcode
{
$asc_chr = [
4,
0,
2,
6,
3,
5,
1,
9,
8,
7,
1,
2,
0,
6,
4,
8,
2,
9,
5,
3,
0,
1,
3,
7,
4,
6,
8,
9,
2,
0,
5,
1,
9,
4,
3,
8,
6,
7,
1,
2,
4,
3,
9,
5,
7,
8,
3,
0,
2,
1,
4,
0,
9,
1,
7,
0,
2,
4,
6,
3,
7,
1,
9,
5,
8,
];
$dsc_chr = [
7,
1,
9,
5,
8,
0,
2,
4,
6,
3,
5,
8,
9,
7,
3,
0,
6,
1,
7,
4,
6,
8,
9,
2,
5,
1,
7,
5,
4,
3,
8,
7,
6,
0,
2,
5,
4,
9,
3,
0,
1,
6,
8,
2,
0,
4,
5,
9,
6,
7,
5,
2,
6,
3,
8,
5,
1,
9,
8,
7,
4,
0,
2,
6,
3,
];
$asc_pos = [
3,
0,
8,
11,
1,
12,
8,
11,
10,
6,
4,
12,
2,
7,
9,
6,
7,
9,
2,
8,
4,
0,
12,
7,
10,
9,
0,
7,
10,
5,
7,
9,
6,
8,
2,
12,
1,
4,
2,
0,
1,
5,
4,
6,
12,
1,
0,
9,
4,
7,
5,
10,
2,
6,
9,
11,
2,
12,
6,
7,
5,
11,
0,
3,
2,
];
$dsc_pos = [
2,
10,
12,
5,
9,
1,
5,
4,
3,
9,
11,
5,
10,
1,
6,
3,
4,
1,
10,
0,
2,
11,
8,
6,
1,
12,
3,
8,
6,
4,
4,
11,
0,
6,
1,
9,
11,
5,
3,
7,
3,
10,
7,
11,
8,
2,
10,
3,
5,
8,
0,
3,
12,
11,
8,
4,
5,
1,
3,
0,
7,
12,
9,
8,
10,
];
$code_arr = explode('-', $code);
$tracking_number = $code_arr[0];
$routing_code = $code_arr[1] ?? '';
// Conversion of Routing Code
$binary_code = match (strlen($routing_code)) {
0 => 0,
5 => bcadd($routing_code, '1'),
9 => bcadd($routing_code, '100001'),
11 => bcadd($routing_code, '1000100001'),
default => throw new BarcodeException('Routing code unknown'),
};
$binary_code = bcmul($binary_code, 10);
$binary_code = bcadd($binary_code, $tracking_number[0]);
$binary_code = bcmul($binary_code, 5);
$binary_code = bcadd($binary_code, $tracking_number[1]);
$binary_code .= substr($tracking_number, 2, 18);
// convert to hexadecimal
$binary_code = $this->dec_to_hex($binary_code);
// pad to get 13 bytes
$binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT);
// convert string to array of bytes
$binary_code_arr = chunk_split($binary_code, 2, "\r");
$binary_code_arr = substr($binary_code_arr, 0, -1);
$binary_code_arr = explode("\r", $binary_code_arr);
// calculate frame check sequence
$fcs = $this->imb_crc11fcs($binary_code_arr);
// exclude first 2 bits from first byte
$first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2));
$binary_code_102bit = $first_byte.substr($binary_code, 2);
// convert binary data to codewords
$codewords = [];
$data = $this->hex_to_dec($binary_code_102bit);
$codewords[0] = bcmod($data, 636) * 2;
$data = bcdiv($data, 636);
for ($i = 1; $i < 9; $i++) {
$codewords[$i] = bcmod($data, 1365);
$data = bcdiv($data, 1365);
}
$codewords[9] = $data;
if (($fcs >> 10) === 1) {
$codewords[9] += 659;
}
// generate lookup tables
$table2of13 = $this->imb_tables(2, 78);
$table5of13 = $this->imb_tables(5, 1287);
// convert codewords to characters
$characters = [];
$bitmask = 512;
foreach ($codewords as $val) {
if ($val <= 1286) {
$chrcode = $table5of13[$val];
} else {
$chrcode = $table2of13[($val - 1287)];
}
if (($fcs & $bitmask) > 0) {
// bitwise invert
$chrcode = ((~$chrcode) & 8191);
}
$characters[] = $chrcode;
$bitmask /= 2;
}
$characters = array_reverse($characters);
// build bars
$barcode = new Barcode($code);
for ($i = 0; $i < 65; $i++) {
$asc = (($characters[$asc_chr[$i]] & (2 ** $asc_pos[$i])) > 0);
$dsc = (($characters[$dsc_chr[$i]] & (2 ** $dsc_pos[$i])) > 0);
if ($asc && $dsc) {
// full bar (F)
$p = 0;
$h = 3;
} elseif ($asc) {
// ascender (A)
$p = 0;
$h = 2;
} elseif ($dsc) {
// descender (D)
$p = 1;
$h = 2;
} else {
// tracker (T)
$p = 1;
$h = 1;
}
$barcode->addBar(new BarcodeBar(1, $h, true, $p));
if ($i < 64) {
$barcode->addBar(new BarcodeBar(1, 2, false, 0));
}
}
return $barcode;
}
/**
* Convert large integer number to hexadecimal representation.
* (requires PHP bcmath extension)
*
* @param $number (string) number to convert specified as a string
*/
protected function dec_to_hex(string $number): string
{
if ($number === '0') {
return '00';
}
$hex = [];
while ($number > 0) {
$hex[] = strtoupper(dechex(bcmod($number, '16')));
$number = bcdiv($number, '16');
}
$hex = array_reverse($hex);
return implode($hex);
}
/**
* Intelligent Mail Barcode calculation of Frame Check Sequence
*
* @param $code_arr array of hexadecimal values (13 bytes holding 102 bits right justified).
* @return int 11 bit Frame Check Sequence as integer (decimal base)
*/
protected function imb_crc11fcs(array $code_arr): int
{
$genpoly = 0x0F35; // generator polynomial
$fcs = 0x07FF; // Frame Check Sequence
// do most significant byte skipping the 2 most significant bits
$data = hexdec($code_arr[0]) << 5;
for ($bit = 2; $bit < 8; $bit++) {
if (($fcs ^ $data) & 0x400) {
$fcs = ($fcs << 1) ^ $genpoly;
} else {
$fcs <<= 1;
}
$fcs &= 0x7FF;
$data <<= 1;
}
// do rest of bytes
for ($byte = 1; $byte < 13; $byte++) {
$data = hexdec($code_arr[$byte]) << 3;
for ($bit = 0; $bit < 8; $bit++) {
if (($fcs ^ $data) & 0x400) {
$fcs = ($fcs << 1) ^ $genpoly;
} else {
$fcs <<= 1;
}
$fcs &= 0x7FF;
$data <<= 1;
}
}
return $fcs;
}
/**
* Convert large hexadecimal number to decimal representation (string).
* (requires PHP bcmath extension)
*
* @param $hex (string) hexadecimal number to convert specified as a string
* @return int|string hexadecimal representation
*/
protected function hex_to_dec(string $hex): int|string
{
$dec = 0;
$bitval = 1;
$len = strlen($hex);
for ($pos = ($len - 1); $pos >= 0; $pos--) {
$dec = bcadd($dec, bcmul(hexdec($hex[$pos]), $bitval));
$bitval = bcmul($bitval, 16);
}
return $dec;
}
/**
* generate Nof13 tables used for Intelligent Mail Barcode
*
* @param $n (int) is the type of table: 2 for 2of13 table, 5 for 5of13table
* @param $size (int) size of table (78 for n=2 and 1287 for n=5)
*/
protected function imb_tables(int $n, int $size): array
{
$table = [];
$lli = 0; // LUT lower index
$lui = $size - 1; // LUT upper index
for ($count = 0; $count < 8192; $count++) {
$bit_count = 0;
for ($bit_index = 0; $bit_index < 13; $bit_index++) {
$bit_count += (int) (($count & (1 << $bit_index)) !== 0);
}
// if we don't have the right number of bits on, go on to the next value
if ($bit_count === $n) {
$reverse = ($this->imb_reverse_us($count) >> 3);
// if the reverse is less than count, we have already visited this pair before
if ($reverse >= $count) {
// If count is symmetric, place it at the first free slot from the end of the list.
// 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
if ($reverse === $count) {
$table[$lui] = $count;
$lui--;
} else {
$table[$lli] = $count;
$lli++;
$table[$lli] = $reverse;
$lli++;
}
}
}
}
return $table;
}
/**
* Reverse unsigned short value
*
* @param $num (int) value to reverse
*/
protected function imb_reverse_us(int $num): int
{
$rev = 0;
for ($i = 0; $i < 16; $i++) {
$rev <<= 1;
$rev |= ($num & 1);
$num >>= 1;
}
return $rev;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
interface TypeInterface
{
public function getBarcodeData(string $code): Barcode;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* Interleaved 2 of 5 barcodes.
* Compact numeric code, widely used in industry, air cargo
* Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
*/
class TypeInterleaved25 extends TypeInterleaved25Checksum
{
protected function getChecksum(string $code): string
{
return '';
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
use AgeekDev\Barcode\Helpers\BarcodeHelper;
/*
* Interleaved 2 of 5 barcodes.
* Compact numeric code, widely used in industry, air cargo
* Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
*/
class TypeInterleaved25Checksum implements TypeInterface
{
/**
* @throws InvalidCharacterException
*/
public function getBarcodeData(string $code): Barcode
{
$chr['0'] = '11221';
$chr['1'] = '21112';
$chr['2'] = '12112';
$chr['3'] = '22111';
$chr['4'] = '11212';
$chr['5'] = '21211';
$chr['6'] = '12211';
$chr['7'] = '11122';
$chr['8'] = '21121';
$chr['9'] = '12121';
$chr['A'] = '11';
$chr['Z'] = '21';
// add checksum
$code .= $this->getChecksum($code);
if ((strlen($code) % 2) != 0) {
// add leading zero if code-length is odd
$code = '0'.$code;
}
// add start and stop codes
$code = 'AA'.strtolower($code).'ZA';
$barcode = new Barcode($code);
for ($i = 0, $iMax = strlen($code); $i < $iMax; $i = ($i + 2)) {
$char_bar = $code[$i];
$char_space = $code[$i + 1];
if (! isset($chr[$char_bar], $chr[$char_space])) {
throw new InvalidCharacterException;
}
// create a bar-space sequence
$seq = '';
$chrlen = strlen($chr[$char_bar]);
for ($s = 0; $s < $chrlen; $s++) {
$seq .= $chr[$char_bar][$s].$chr[$char_space][$s];
}
for ($j = 0, $jMax = strlen($seq); $j < $jMax; $j++) {
if (($j % 2) === 0) {
$t = true; // bar
} else {
$t = false; // space
}
$w = $seq[$j];
$barcode->addBar(new BarcodeBar($w, 1, $t));
}
}
return $barcode;
}
protected function getChecksum(string $code): string
{
return (string) BarcodeHelper::getChecksum($code);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* RMS4CC - CBC - KIX
* RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
* RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
* @param $kix (boolean) if true prints the KIX variation (doesn't use the start and end symbols, and the checksum)
* - in this case the house number must be sufficed with an X and placed at the end of the code.
*/
class TypeKix extends TypeRms4cc
{
protected bool $kix = true;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* MSI.
* Variation of Plessey code, with similar applications
* Contains digits (0 to 9) and encodes the data only in the width of bars.
*
* @param $code (string) code to represent.
* @param $checksum (boolean) if true add a checksum to the code (modulo 11)
*/
class TypeMsi extends TypeMsiChecksum
{
protected bool $checksum = false;
}

View File

@@ -0,0 +1,74 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
use AgeekDev\Barcode\Helpers\BinarySequenceConverter;
/*
* MSI.
* Variation of Plessey code, with similar applications
* Contains digits (0 to 9) and encodes the data only in the width of bars.
*
* @param $code (string) code to represent.
* @param $checksum (boolean) if true add a checksum to the code (modulo 11)
*/
class TypeMsiChecksum implements TypeInterface
{
protected bool $checksum = true;
/**
* @throws InvalidCharacterException
*/
public function getBarcodeData(string $code): Barcode
{
$chr['0'] = '100100100100';
$chr['1'] = '100100100110';
$chr['2'] = '100100110100';
$chr['3'] = '100100110110';
$chr['4'] = '100110100100';
$chr['5'] = '100110100110';
$chr['6'] = '100110110100';
$chr['7'] = '100110110110';
$chr['8'] = '110100100100';
$chr['9'] = '110100100110';
$chr['A'] = '110100110100';
$chr['B'] = '110100110110';
$chr['C'] = '110110100100';
$chr['D'] = '110110100110';
$chr['E'] = '110110110100';
$chr['F'] = '110110110110';
if ($this->checksum) {
// add checksum
$clen = strlen($code);
$p = 2;
$check = 0;
for ($i = ($clen - 1); $i >= 0; $i--) {
$check += (hexdec($code[$i]) * $p);
$p++;
if ($p > 7) {
$p = 2;
}
}
$check %= 11;
if ($check > 0) {
$check = 11 - $check;
}
$code .= $check;
}
$seq = '110'; // left guard
$clen = strlen($code);
for ($i = 0; $i < $clen; $i++) {
$digit = $code[$i];
if (! isset($chr[$digit])) {
throw new InvalidCharacterException('Char '.$digit.' is unsupported');
}
$seq .= $chr[$digit];
}
$seq .= '1001'; // right guard
return BinarySequenceConverter::convert($code, $seq);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\Helpers\BinarySequenceConverter;
/*
* Pharmacode
* Contains digits (0 to 9)
*/
class TypePharmacode implements TypeInterface
{
public function getBarcodeData(string|int $code): Barcode
{
$code = (int) $code;
$seq = '';
while ($code > 0) {
if (($code % 2) === 0) {
$seq .= '11100';
$code -= 2;
} else {
$seq .= '100';
$code--;
}
$code /= 2;
}
$seq = substr($seq, 0, -2);
$seq = strrev($seq);
return BinarySequenceConverter::convert($code, $seq);
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* Pharmacode two-track
* Contains digits (0 to 9)
*/
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
use AgeekDev\Barcode\Exceptions\InvalidLengthException;
class TypePharmacodeTwoCode implements TypeInterface
{
/**
* @throws InvalidLengthException
*/
public function getBarcodeData(string|float $code): Barcode
{
$code = (float) $code;
if ($code < 1) {
throw new InvalidLengthException('Pharmacode 2 needs a number of 1 or larger');
}
$seq = '';
do {
switch ($code % 3) {
case 0:
$seq .= '3';
$code = ($code - 3) / 3.0;
break;
case 1:
$seq .= '1';
$code = ($code - 1) / 3.0;
break;
case 2:
$seq .= '2';
$code = ($code - 2) / 3.0;
break;
}
} while ($code != 0);
$seq = strrev($seq);
$barcode = new Barcode($code);
$p = 0;
$h = 1;
for ($i = 0, $iMax = strlen($seq); $i < $iMax; $i++) {
switch ($seq[$i]) {
case '1':
$p = 1;
break;
case '2':
$p = 0;
break;
case '3':
$h = 2;
break;
}
$barcode->addBar(new BarcodeBar(1, $h, 1, $p));
if ($i < (strlen($seq) - 1)) {
$barcode->addBar(new BarcodeBar(1, 2, 0, 0));
}
}
return $barcode;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* PLANET barcodes.
* Used by U.S. Postal Service for automated mail sorting
*
* @param $code (string) zip code to represent. Must be a string containing a zip code of the form DDDDD or
* DDDDD-DDDD.
* @param $planet (boolean) if true print the PLANET barcode, otherwise print POSTNET
*/
class TypePlanet extends TypePostnet
{
protected array $barlen = [
0 => [1, 1, 2, 2, 2],
1 => [2, 2, 2, 1, 1],
2 => [2, 2, 1, 2, 1],
3 => [2, 2, 1, 1, 2],
4 => [2, 1, 2, 2, 1],
5 => [2, 1, 2, 1, 2],
6 => [2, 1, 1, 2, 2],
7 => [1, 2, 2, 2, 1],
8 => [1, 2, 2, 1, 2],
9 => [1, 2, 1, 2, 2],
];
}

View File

@@ -0,0 +1,69 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* POSTNET and PLANET barcodes.
* Used by U.S. Postal Service for automated mail sorting
*
* @param $code (string) zip code to represent. Must be a string containing a zip code of the form DDDDD or
* DDDDD-DDDD.
* @param $planet (boolean) if true print the PLANET barcode, otherwise print POSTNET
*/
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
class TypePostnet implements TypeInterface
{
protected array $barlen = [
0 => [2, 2, 1, 1, 1],
1 => [1, 1, 1, 2, 2],
2 => [1, 1, 2, 1, 2],
3 => [1, 1, 2, 2, 1],
4 => [1, 2, 1, 1, 2],
5 => [1, 2, 1, 2, 1],
6 => [1, 2, 2, 1, 1],
7 => [2, 1, 1, 1, 2],
8 => [2, 1, 1, 2, 1],
9 => [2, 1, 2, 1, 1],
];
public function getBarcodeData(string $code): Barcode
{
$code = str_replace(['-', ' '], '', $code);
$len = strlen($code);
$barcode = new Barcode($code);
// calculate checksum
$sum = 0;
for ($i = 0; $i < $len; $i++) {
$sum += (int) $code[$i];
}
$chkd = ($sum % 10);
if ($chkd > 0) {
$chkd = (10 - $chkd);
}
$code .= $chkd;
$len = strlen($code);
// start bar
$barcode->addBar(new BarcodeBar(1, 2, 1));
$barcode->addBar(new BarcodeBar(1, 2, 0));
for ($i = 0; $i < $len; $i++) {
for ($j = 0; $j < 5; $j++) {
$h = $this->barlen[$code[$i]][$j];
$p = floor(1 / $h);
$barcode->addBar(new BarcodeBar(1, $h, 1, $p));
$barcode->addBar(new BarcodeBar(1, 2, 0));
}
}
// end bar
$barcode->addBar(new BarcodeBar(1, 2, 1));
return $barcode;
}
}

View File

@@ -0,0 +1,165 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* RMS4CC - CBC - KIX
* RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
* RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
* @param $kix (boolean) if true prints the KIX variation (doesn't use the start and end symbols, and the checksum)
* - in this case the house number must be sufficed with an X and placed at the end of the code.
*/
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\BarcodeBar;
class TypeRms4cc implements TypeInterface
{
protected bool $kix = false;
public function getBarcodeData(string $code): Barcode
{
// bar mode
// 1 = pos 1, length 2
// 2 = pos 1, length 3
// 3 = pos 2, length 1
// 4 = pos 2, length 2
$barmode = [
'0' => [3, 3, 2, 2],
'1' => [3, 4, 1, 2],
'2' => [3, 4, 2, 1],
'3' => [4, 3, 1, 2],
'4' => [4, 3, 2, 1],
'5' => [4, 4, 1, 1],
'6' => [3, 1, 4, 2],
'7' => [3, 2, 3, 2],
'8' => [3, 2, 4, 1],
'9' => [4, 1, 3, 2],
'A' => [4, 1, 4, 1],
'B' => [4, 2, 3, 1],
'C' => [3, 1, 2, 4],
'D' => [3, 2, 1, 4],
'E' => [3, 2, 2, 3],
'F' => [4, 1, 1, 4],
'G' => [4, 1, 2, 3],
'H' => [4, 2, 1, 3],
'I' => [1, 3, 4, 2],
'J' => [1, 4, 3, 2],
'K' => [1, 4, 4, 1],
'L' => [2, 3, 3, 2],
'M' => [2, 3, 4, 1],
'N' => [2, 4, 3, 1],
'O' => [1, 3, 2, 4],
'P' => [1, 4, 1, 4],
'Q' => [1, 4, 2, 3],
'R' => [2, 3, 1, 4],
'S' => [2, 3, 2, 3],
'T' => [2, 4, 1, 3],
'U' => [1, 1, 4, 4],
'V' => [1, 2, 3, 4],
'W' => [1, 2, 4, 3],
'X' => [2, 1, 3, 4],
'Y' => [2, 1, 4, 3],
'Z' => [2, 2, 3, 3],
];
$code = strtoupper($code);
$len = strlen($code);
$barcode = new Barcode($code);
if (! $this->kix) {
// table for checksum calculation (row,col)
$checktable = [
'0' => [1, 1],
'1' => [1, 2],
'2' => [1, 3],
'3' => [1, 4],
'4' => [1, 5],
'5' => [1, 0],
'6' => [2, 1],
'7' => [2, 2],
'8' => [2, 3],
'9' => [2, 4],
'A' => [2, 5],
'B' => [2, 0],
'C' => [3, 1],
'D' => [3, 2],
'E' => [3, 3],
'F' => [3, 4],
'G' => [3, 5],
'H' => [3, 0],
'I' => [4, 1],
'J' => [4, 2],
'K' => [4, 3],
'L' => [4, 4],
'M' => [4, 5],
'N' => [4, 0],
'O' => [5, 1],
'P' => [5, 2],
'Q' => [5, 3],
'R' => [5, 4],
'S' => [5, 5],
'T' => [5, 0],
'U' => [0, 1],
'V' => [0, 2],
'W' => [0, 3],
'X' => [0, 4],
'Y' => [0, 5],
'Z' => [0, 0],
];
$row = 0;
$col = 0;
for ($i = 0; $i < $len; $i++) {
$row += $checktable[$code[$i]][0];
$col += $checktable[$code[$i]][1];
}
$row %= 6;
$col %= 6;
$chk = array_keys($checktable, [$row, $col]);
$code .= $chk[0];
$len++;
// start bar
$barcode->addBar(new BarcodeBar(1, 2, 1));
$barcode->addBar(new BarcodeBar(1, 2, 0));
}
for ($i = 0; $i < $len; $i++) {
for ($j = 0; $j < 4; $j++) {
switch ($barmode[$code[$i]][$j]) {
case 1:
$p = 0;
$h = 2;
break;
case 2:
$p = 0;
$h = 3;
break;
case 3:
$p = 1;
$h = 1;
break;
case 4:
$p = 1;
$h = 2;
break;
}
$barcode->addBar(new BarcodeBar(1, $h, 1, $p));
$barcode->addBar(new BarcodeBar(1, 2, 0));
}
}
if (! $this->kix) {
// stop bar
$barcode->addBar(new BarcodeBar(1, 3, 1));
}
return $barcode;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\Exceptions\InvalidCharacterException;
use AgeekDev\Barcode\Helpers\BarcodeHelper;
use AgeekDev\Barcode\Helpers\BinarySequenceConverter;
/*
* Standard 2 of 5 barcodes.
* Used in airline ticket marking, photofinishing
* Contains digits (0 to 9) and encodes the data only in the width of bars.
*/
class TypeStandard2of5 implements TypeInterface
{
protected bool $checksum = false;
/**
* @throws InvalidCharacterException
*/
public function getBarcodeData(string $code): Barcode
{
$chr['0'] = '10101110111010';
$chr['1'] = '11101010101110';
$chr['2'] = '10111010101110';
$chr['3'] = '11101110101010';
$chr['4'] = '10101110101110';
$chr['5'] = '11101011101010';
$chr['6'] = '10111011101010';
$chr['7'] = '10101011101110';
$chr['8'] = '11101010111010';
$chr['9'] = '10111010111010';
if ($this->checksum) {
// add checksum
$code .= $this->checksum_s25($code);
}
$seq = '11011010';
for ($i = 0, $iMax = strlen($code); $i < $iMax; $i++) {
$digit = $code[$i];
if (! isset($chr[$digit])) {
throw new InvalidCharacterException('Char '.$digit.' is unsupported');
}
$seq .= $chr[$digit];
}
$seq .= '1101011';
return BinarySequenceConverter::convert($code, $seq);
}
/**
* Checksum for standard 2 of 5 barcodes.
*
* @param $code (string) code to process.
* @return int checksum.
*/
protected function checksum_s25(string $code): int
{
return BarcodeHelper::getChecksum($code);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* Standard 2 of 5 barcodes.
* Used in airline ticket marking, photofinishing
* Contains digits (0 to 9) and encodes the data only in the width of bars.
*/
class TypeStandard2of5Checksum extends TypeStandard2of5
{
protected bool $checksum = true;
}

View File

@@ -0,0 +1,22 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* EAN13 and UPC-A barcodes.
* EAN13: European Article Numbering international retail product code
* UPC-A: Universal product code seen on almost all retail products in the USA and Canada
* UPC-E: Short version of UPC symbol
*
* @param $code (string) code to represent.
* @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
*/
class TypeUpcA extends TypeEanUpcBase
{
protected int $length = 12;
protected bool $upca = true;
protected bool $upce = false;
}

View File

@@ -0,0 +1,22 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* EAN13 and UPC-A barcodes.
* EAN13: European Article Numbering international retail product code
* UPC-A: Universal product code seen on almost all retail products in the USA and Canada
* UPC-E: Short version of UPC symbol
*
* @param $code (string) code to represent.
* @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
*/
class TypeUpcE extends TypeEanUpcBase
{
protected int $length = 12;
protected bool $upca = false;
protected bool $upce = true;
}

View File

@@ -0,0 +1,98 @@
<?php
namespace AgeekDev\Barcode\Types;
use AgeekDev\Barcode\Barcode;
use AgeekDev\Barcode\Exceptions\InvalidCheckDigitException;
use AgeekDev\Barcode\Helpers\BinarySequenceConverter;
/*
* UPC-Based Extensions
* 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
* 5-Digit Ext.: Used to mark suggested retail price of books
*/
class TypeUpcExtension2 implements TypeInterface
{
protected int $length = 2;
/**
* @throws InvalidCheckDigitException
*/
public function getBarcodeData(string $code): Barcode
{
$len = $this->length;
// Padding
$code = str_pad($code, $len, '0', STR_PAD_LEFT);
// calculate check digit
if ($len === 2) {
$r = (int) $code % 4;
} elseif ($len === 5) {
$r = (3 * ((int) $code[0] + (int) $code[2] + (int) $code[4])) + (9 * ((int) $code[1] + (int) $code[3]));
$r %= 10;
} else {
throw new InvalidCheckDigitException;
}
// Convert digits to bars
$codes = [
'A' => [ // left odd parity
'0' => '0001101',
'1' => '0011001',
'2' => '0010011',
'3' => '0111101',
'4' => '0100011',
'5' => '0110001',
'6' => '0101111',
'7' => '0111011',
'8' => '0110111',
'9' => '0001011',
],
'B' => [ // left even parity
'0' => '0100111',
'1' => '0110011',
'2' => '0011011',
'3' => '0100001',
'4' => '0011101',
'5' => '0111001',
'6' => '0000101',
'7' => '0010001',
'8' => '0001001',
'9' => '0010111',
],
];
$parities = [
2 => [
'0' => ['A', 'A'],
'1' => ['A', 'B'],
'2' => ['B', 'A'],
'3' => ['B', 'B'],
],
5 => [
'0' => ['B', 'B', 'A', 'A', 'A'],
'1' => ['B', 'A', 'B', 'A', 'A'],
'2' => ['B', 'A', 'A', 'B', 'A'],
'3' => ['B', 'A', 'A', 'A', 'B'],
'4' => ['A', 'B', 'B', 'A', 'A'],
'5' => ['A', 'A', 'B', 'B', 'A'],
'6' => ['A', 'A', 'A', 'B', 'B'],
'7' => ['A', 'B', 'A', 'B', 'A'],
'8' => ['A', 'B', 'A', 'A', 'B'],
'9' => ['A', 'A', 'B', 'A', 'B'],
],
];
$p = $parities[$len][$r];
$seq = '1011'; // left guard bar
$seq .= $codes[$p[0]][$code[0]];
for ($i = 1; $i < $len; $i++) {
$seq .= '01'; // separator
$seq .= $codes[$p[$i]][$code[$i]];
}
return BinarySequenceConverter::convert($code, $seq);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace AgeekDev\Barcode\Types;
/*
* UPC-Based Extensions
* 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
* 5-Digit Ext.: Used to mark suggested retail price of books
*/
class TypeUpcExtension5 extends TypeUpcExtension2
{
protected int $length = 5;
}