Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tc Lib Barcode Laravel Package

tecnickcom/tc-lib-barcode

Pure-PHP library that encodes data as linear, 2D and postal barcodes and renders them as SVG, PNG, HTML or text. Supports CODE 39/128, EAN, UPC, QR Code and more, part of the TCPDF ecosystem.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require tecnickcom/tc-lib-barcode

Requirements to verify on the box before you write code: PHP ≥ 8.2, the gd extension (or imagick), and bcmath if you plan on PDF417 or IMB. composer show tecnickcom/tc-lib-barcode plus php -m | grep -E 'gd|bcmath' is your first five minutes.

There is nothing to publish: the library is a plain PHP namespace (\Com\Tecnick\Barcode) already autoloaded by Composer. Register the one entry point as a singleton so you can inject it and swap it in tests:

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->singleton(\Com\Tecnick\Barcode\Barcode::class);
}

The first use case that will land on your desk is almost certainly a QR code. The whole library collapses to one call, and the returned object is the thing you configure and render:

$bobj = app(\Com\Tecnick\Barcode\Barcode::class)->getBarcodeObj(
    type: 'QRCODE,H',                 // QRCODE,L/M/Q/H error correction
    code: 'https://example.com/orders/1042',
    width: -4,                        // negative = stretch to fit
    height: -4,
    color: 'black',
    padding: [-2, -2, -2, -2],        // top, right, bottom, left
)->setBackgroundColor('white');

return response($bobj->getPngData(), 200, ['Content-Type' => 'image/png']);

Where to look first: the Supported Formats table in the README (that's your type vocabulary), then getBarcodeObj's signature, then the render methods on the returned object — getPngData(), getSvgCode(), getInlineSvgCode() (a data-URI you can drop into <img src>), getHtmlDiv(), getUnicode() for terminal/grid output, and getBarcodeArray() if you ever need raw bar coordinates. Anything not covered in the README lives in the repo's example/ directory and at https://tcpdf.org/docs/srcdoc/tc-lib-barcode.

Implementation Patterns

Wrap it, don't call it from controllers. One thin service gives you a single place to pin colors, quiet zones, and the type whitelist, and a single place to fake in tests.

namespace App\Services;

use Com\Tecnick\Barcode\Barcode;

class BarcodeService
{
    public function __construct(private Barcode $lib) {}

    public function obj(string $type, string $code, int $size = -3, string $color = 'black')
    {
        return $this->lib->getBarcodeObj($type, $code, $size, $size, $color, [-2, -2, -2, -2])
            ->setBackgroundColor('white');
    }

    public function png(string $type, string $code, int $size = -3): string
    {
        return $this->obj($type, $code, $size)->getPngData();
    }

    public function svg(string $type, string $code, int $size = -3): string
    {
        return $this->obj($type, $code, $size)->getSvgCode();
    }

    public function inlineSvg(string $type, string $code, int $size = -3): string
    {
        return $this->obj($type, $code, $size)->getInlineSvgCode();
    }
}

Serve barcodes from a route, not from every view. Barcode generation is deterministic — the same inputs always yield the same pixels — which makes it a perfect HTTP-cacheable resource. Encode the payload in a signed URL or an opaque token, then generate on miss:

Route::get('/b/{token}.png', function (string $token, BarcodeService $bc) {
    $payload = Cache::rememberForever("bc:{$token}", fn () => SignedPayload::decode($token));

    return response($bc->png($payload['type'], $payload['code']), 200, [
        'Content-Type'        => 'image/png',
        'Cache-Control'       => 'public, max-age=31536000, immutable',
        'Content-Disposition' => 'inline; filename="barcode.png"',
    ]);
})->middleware('throttle:60,1')->name('barcode.png');

This is the pattern that scales: the browser and CDN absorb repeat views, and your PHP process touches the encoder once per unique payload.

Blade component for inline use. SVG is the right default for web (crisp at any DPI, no GD hop, ~10× smaller than a PNG for a QR code):

class Barcode extends Component
{
    public function __construct(
        public string $value,
        public string $type = 'QRCODE,H',
        public int $size = -3,
    ) {}

    public function render(BarcodeService $bc)
    {
        return view('components.barcode', [
            'src' => $bc->inlineSvg($this->type, $this->value, $this->size),
        ]);
    }
}
{{-- resources/views/components/barcode.blade.php --}}
<img src="{{ $src }}" alt="Barcode {{ $value }}" style="width: 100%; max-width: 240px;">

Using getInlineSvgCode() sidesteps the {!! !!} raw-output hole you'd otherwise open up with getSvgCode().

Batch generation belongs in a queued job. For an inventory export, chunk by primary key, add straight into a ZipArchive, and write to Storage. Never loop getPngData() over 10k rows in a web request — a single QRCODE at -4 is cheap, but PDF417 and IMB are not, and PNG allocation is the memory hog.

Product::query()->chunkById(500, function ($products) use ($zip, $bc) {
    foreach ($products as $p) {
        $zip->addFromString("{$p->sku}.png", $bc->png('C128B', $p->sku));
    }
});

Where the output goes:

  • Web pages / responsive layouts → inline SVG.
  • Emails → hosted PNG URL (many clients strip data: URIs and SVG entirely).
  • PDFs (dompdf, Snappy, or the tc-lib-pdf sibling) → PNG; SVG support in PDF renderers is inconsistent.
  • Warehouse/label printers → PNG at a large negative size, or the text grid via getUnicode() for terminal previews.

Testing. You don't need to scan barcodes in CI (though a manual scan per format on release is cheap insurance). Assert the container, not the pixels:

$response = $this->get("/b/{$token}.png")->assertHeader('Content-Type', 'image/png');
$this->assertStringStartsWith("\x89PNG", $response->getContent());

$svg = app(BarcodeService::class)->svg('QRCODE,H', 'hello');
$this->assertStringContainsString('<svg', $svg);

Gotchas and Tips

Whitelist the type argument. The type string is used to resolve which encoder class gets loaded, so passing user input straight through is a class-resolution surface you don't want. Validate with Rule::in([...]) against a config array of the formats you actually support, and default to QRCODE,H.

Negative width/height are a feature, not a typo. Positive values size individual bar modules; negative values stretch the whole symbol to fit the given box. Mixing them (positive width, negative height) is legal but produces surprising aspect ratios. For anything responsive, use negative on both.

Padding controls the quiet zone — and quiet zones are scannability. Negative padding crops whitespace, which is great for tight UI but will silently break EAN-13/UPC-A scanners in the real world. Keep at least one module of quiet zone for linear retail codes; crop freely for QR.

Length and checksum rules are enforced by the encoder, not by you. EAN13 wants 12 digits (it appends the check digit) or a valid 13; I25 needs an even digit count; PHARMA has a numeric range. Violations throw — catch around generation and return a 422, otherwise a bad admin form entry becomes a 500.

PNG without GD fails at runtime, not at install time. Composer won't stop you. If you deploy to a minimal container, either add gd (or imagick) to the image or force SVG everywhere. Similarly, bcmath is optional but noticeably speeds up PDF417 and IMB — add it if those formats are on your roadmap.

Cache aggressively by payload hash. Deterministic output means md5($type.'|'.$code.'|'.$size.'|'.$color) is a safe cache key. Store the PNG/SVG bytes in Redis or on disk for repeat-heavy pages (ticket stubs, product labels) rather than re-encoding on every request.

Don't emit anything before the binary response. A stray dd(), BOM, or whitespace in a service provider turns getPngData() into a corrupt image. If you're debugging, write to storage/app/debug.png with Storage::put() instead of echo.

Version pin and watch for drift. Releases are infrequent and the surface is stable, but pin to a minor (^2.16) and run your SVG/PNG assertion tests on upgrade — output geometry can shift between versions without any API change, which matters if you've snapshotted label templates.

License: LGPL-3.0, despite the "NOASSERTION" tag on Packagist. Consuming it as an unmodified Composer dependency from a Laravel app is the normal, intended use. If your legal team has a hard rule against LGPL in distributed products, get a written opinion early rather than discovering it at release; forking and modifying it would trigger the share-back obligation.

Extension points are shallow on purpose. The realistic customization layer is your BarcodeService — colors, padding, size presets, type aliases ('qr' => 'QRCODE,H', 'shipping' => 'C128B'). If you need a symbology the library doesn't ship, that's a fork or a different package, not a subclass — the encoder internals are tightly coupled to the type registry.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core