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,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;
}