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

Laminas Barcode Laravel Package

laminas/laminas-barcode

laminas/laminas-barcode is a framework-agnostic PHP library for generating and rendering barcodes, supporting common symbologies such as Code 39, Code 128, EAN, UPC, and QR, with configurable image, PDF, and HTML renderers.

Deep Wiki
Context7

Getting Started

laminas/laminas-barcode is a framework-agnostic component — there is no Laravel service provider, no facade, no published config. You composer require laminas/laminas-barcode and instantiate classes directly. That is the whole integration.

composer require laminas/laminas-barcode
# ext-gd is required only for the Image renderer
php -m | grep -i gd

The two entry points you should read first:

  • src/Barcode.php — the static factory: Barcode::make($type, $options) and Barcode::render($type, $rendererName, $barcodeOptions, $rendererOptions).
  • src/Object/* — one class per symbology (Code128, Code39, Code25, Ean13, Ean8, UpcA, Itf14, Codabar, Postnet, …).
  • src/Renderer/* — Image, Svg, Html, Pdf. Pick the renderer per use case, not the symbology.

First use case — a signed-internal PNG endpoint that renders a SKU as Code 128:

use Laminas\Barcode\Barcode;
use Laminas\Barcode\Renderer\Image;

Route::get('/barcodes/{sku}.png', function (string $sku) {
    $barcode = Barcode::make('code128', [
        'text'     => $sku,
        'drawText' => true,
        'factor'   => 2,       // bar width multiplier
        'barHeight' => 80,
    ]);

    $renderer = new Image();
    $renderer->setBarcode($barcode);
    $renderer->setImageType('png');

    // The Image renderer streams to the output buffer; capture it.
    ob_start();
    $renderer->render();
    $png = ob_get_clean();

    return response($png, 200, ['Content-Type' => 'image/png']);
})->where('sku', '[A-Za-z0-9\-]+');

The docs live at docs.laminas.dev/laminas-barcode (usage, per-symbology option tables, renderer options). Read the renderer section before the symbology section — most "it renders but scanners reject it" problems are renderer sizing, not encoding.

Implementation Patterns

Wrap it in one small service, bind it as a singleton. You will call it from controllers, jobs, Blade directives, and PDF builders; duplicating the ob_start() dance in five places is how bugs creep in.

final class BarcodeService
{
    public function png(string $type, string $text, array $options = []): string
    {
        $barcode = Barcode::make($type, $options + config('barcode.defaults') + ['text' => $text]);
        $renderer = new \Laminas\Barcode\Renderer\Image();
        $renderer->setBarcode($barcode);
        $renderer->setImageType('png');

        ob_start();
        $renderer->render();
        return (string) ob_get_clean();
    }

    public function svg(string $type, string $text, array $options = []): string
    {
        $renderer = new \Laminas\Barcode\Renderer\Svg();
        $renderer->setBarcode(Barcode::make($type, $options + ['text' => $text]));
        return $renderer->render(); // Svg returns a string
    }
}

Choose the renderer by output channel, not by preference:

Channel Renderer Why
HTTP image, print labels Image Raster PNG/JPEG/GIF, works in every PDF/label tool
Inline in Blade, email, SVG logos Svg Returns a string, scales without blur, no GD
HTML email without GD Html Returns an HTML table of bar <div>s
Existing PDF pipeline Pdf Only if you already use laminas/laminas-pdf

Default to SVG for anything that leaves your server as markup, and PNG when a printer, courier API, or label template expects raster.

Cache aggressively. Barcode generation is CPU work on every request and the output is deterministic for a given (type, text, options) tuple. Hash the inputs into the filename and serve from disk/S3:

$key = 'barcodes/' . md5(json_encode([$type, $text, $options])) . '.png';
if (! Storage::exists($key)) {
    Storage::put($key, $service->png($type, $text, $options));
}
return Storage::response($key, null, ['Cache-Control' => 'public, max-age=31536000, immutable']);

Validate before you render. laminas/laminas-validator ships a Barcode validator backed by the same symbology classes, so the rules (EAN-13 length, UPC checksum) match exactly. Reject with 422 instead of letting Object\Ean13 throw a 500.

$validator = new \Laminas\Validator\Barcode(['barcode' => 'EAN13']);
if (! $validator->isValid($input)) {
    abort(422, implode(' ', $validator->getMessages()));
}

Batch and queue. Generating a 500-label PDF is hundreds of image encodes; do it in a queued job writing to storage, then stream a zip or attach to a mailable. Never generate inside a Blade loop that renders a table of 200 products.

Embedding in PDFs without laminas-pdf: render PNG/SVG, base64 it, and let Dompdf/Snappy handle layout. This is the path most Laravel teams take and it decouples you from the PDF renderer's dependency chain.

Extension points: implement Laminas\Barcode\Renderer\RendererInterface for a custom output target (e.g. an <img> data-URI renderer or a ZPL/EPS emitter), and extend Laminas\Barcode\Object\AbstractObject for a symbology the package doesn't ship.

Gotchas and Tips

  • Renderer\Image::render() writes to output and sends headers. It does not return a string. Always wrap in ob_start()/ob_get_clean(); calling it after any output has started produces a mangled response or "headers already sent".
  • No QR, Aztec, DataMatrix, or PDF417. The package covers 1D retail/industrial symbologies only. For 2D codes use endroid/qr-code or simplesoftwareio/simple-qrcode — do not try to bend Code128 into a QR workflow.
  • Symbology validation is strict and throws. EAN-13 needs 12 or 13 digits (13th is checksum), UPC-A needs 11 or 12, ITF-14 needs 13 or 14. Wrap calls in a try/catch for Laminas\Barcode\Exception\ExceptionInterface and translate to a 422 at the boundary.
  • drawText needs a real TTF font path. Set font (or $renderer->setFont()) to an absolute path such as resource_path('fonts/DejaVuSans.ttf'). The bundled default resolves relative to the vendor tree and breaks in containerized/relocated deploys. If you don't need human-readable text, set drawText => false and sidestep fonts entirely.
  • factor is bar width, barHeight is height — but neither guarantees scannability. Small factors plus image downscaling in CSS or PDF layout is the #1 "it renders fine but the scanner beeps" cause. Keep factor >= 2, barHeight >= 60, and preserve the quiet zone: never crop the whitespace around the barcode.
  • stretchText, drawText, fontSize change appearance only. They never alter the encoded value or checksum. Conversely withChecksum/withChecksumInText on Code39 do change the payload — be explicit when interoperability with an existing scanner fleet matters.
  • The Html renderer emits one <div> per bar, producing very large markup for long values. Fine for a single email, bad inside a loop of 100 rows.
  • PHP 8 returns GdImage objects, not resources. Any legacy is_resource() check, imagedestroy() cleanup, or custom code copying ZF2-era snippets will misfire.
  • The Pdf renderer depends on laminas/laminas-pdf, which is not where Laminas' active investment is. Verify its current status before adopting it; the PNG-into-Dompdf route is lower-risk.
  • Barcode::render(...) and Barcode::make(...) differ in arity — render() takes ($type, $rendererName, $barcodeOptions, $rendererOptions). Passing renderer options as the third argument silently produces a default-sized barcode, which is a confusing debugging session.
  • Testing: golden-file compare the SVG output (stable, text-based, diffable) and assert the encoded value separately via the object's getText()/checksum. Avoid pixel-comparing PNGs — GD version differences flip a pixel and break CI.
  • Debugging a bad barcode: render the same input to SVG and to PNG side by side. If the SVG scans and the PNG doesn't, it is a renderer sizing/DPI problem; if neither scans, it is a symbology/text/checksum problem.
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log