---
title: "ESCPOS Project"
description: "Derafu ESCPOS"
type: "docs"
category: "doc"
tags: [php]
authors: [Anonymous]
date: "2026-09-09"
last_update: "2026-09-09"
time_minutes: 1
draft: false
unlisted: false
url: "https://www.derafu.dev/docs/utils/escpos"
---

# Derafu ESCPOS



---

## Introduction

PHP Library for ESC/POS Printers

# PHP Library for ESC/POS Printers

![GitHub last commit](https://img.shields.io/github/last-commit/derafu/escpos/main)
![CI Workflow](https://github.com/derafu/escpos/actions/workflows/ci.yml/badge.svg?branch=main&amp;event=push)
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/derafu/escpos)
![GitHub Issues](https://img.shields.io/github/issues-raw/derafu/escpos)
![Total Downloads](https://poser.pugx.org/derafu/escpos/downloads)
![Monthly Downloads](https://poser.pugx.org/derafu/escpos/d/monthly)

A PHP library for generating ESC/POS commands to communicate with thermal receipt printers.

Underneath, it uses [mike42/escpos-php](https://github.com/mike42/escpos-php) to talk to the printer, but that library is an implementation detail: nothing in this package&#039;s public API exposes a `Mike42\Escpos\*` type or constant. See [Architecture](./architecture) for why that matters and how it is enforced.

## Features

- Fluent, chainable API for common receipt printing tasks.
- Own vocabulary (enums and constants) for every ESC/POS value, instead of the underlying library&#039;s constants — see [Vocabulary](./vocabulary).
- 1D and 2D barcode generation, including PDF417 (native command or as a printed bitmap when the printer has no native support) — see [Printing Images](./images).
- Sending the generated bytes to a real network printer, without a raw socket or the underlying library&#039;s connectors — see [Network Delivery](./network-delivery).
- Special character handling and replacement.
- Receipt cutting and cash drawer opening.

## Installation

```bash
composer require derafu/escpos
```

## Quick Start

```php
use Derafu\Escpos\Enum\Justification;
use Derafu\Escpos\EscposPrinter;

// Initialize the printer. With no options, it buffers ESC/POS bytes in
// memory instead of talking to a real printer — useful for building the
// receipt and deciding what to do with the bytes afterwards.
$printer = new EscposPrinter();

// Print some text.
$printer
    -&gt;println(&#039;Hello World!&#039;)
    -&gt;setJustification(Justification::Center)
    -&gt;println(&#039;Centered Text&#039;)
    -&gt;println(&#039;Multiple lines&#039;)
    -&gt;feed(2); // Add some empty lines.

// Finalize the receipt (adds final line breaks, cuts paper, opens the
// drawer if configured) and get the ESC/POS bytes.
$escposData = $printer-&gt;end();

// Do whatever you need with the bytes: return them from an API, save them
// to a file, or send them to a real printer (see Network Delivery).
echo $escposData;
```

## Configuration Options

The `EscposPrinter` constructor accepts an options array as its first argument:

| Option         | Type   | Default                                         | Description                                |
|-----------------|--------|--------------------------------------------------|---------------------------------------------|
| `connector`     | array  | `[&#039;type&#039; =&gt; &#039;memory&#039;]`                          | Printer connector configuration. `memory` is the only type supported today — it buffers bytes so `dump()`/`end()` can return them. |
| `profile`       | string | `&#039;default&#039;`                                     | Printer capability profile (from Mike42&#039;s `capabilities.json`). |
| `cut`           | bool   | `true`                                          | Automatically cut the paper when `end()` is called. |
| `specialchars`  | bool   | `true`                                          | When `false`, accented characters (á, ñ, etc.) are replaced by their unaccented equivalent instead of being sent as-is. |
| `pulse`         | array  | `[&#039;pin&#039; =&gt; 0, &#039;on_ms&#039; =&gt; 120, &#039;off_ms&#039; =&gt; 240]` | Cash drawer pulse, sent together with the cut when `cut` is `true`. |

A second, optional constructor argument lets you replace the default backend — see [Architecture](./architecture).

## Full Example

The repository&#039;s `examples/full-example.php` exercises every feature (text formatting, image, barcode, PDF417, special characters). Run it with:

```shell
php examples/full-example.php | nc 172.16.1.5 9100
```

**Note**: you need `netcat` installed, and `172.16.1.5`/`9100` should be the IP and port of your thermal printer. See [Network Delivery](./network-delivery) for a pure-PHP alternative to `nc`.




---

## Vocabulary

Vocabulary

# Vocabulary

Every value `EscposPrinter` accepts (justification, print modes, barcode types, etc.) has its own type under `Derafu\Escpos\Enum\*` or `Derafu\Escpos\Constant\*`. You never need to import anything from `Mike42\Escpos\*` to call any method.

These values are **not arbitrary numbers chosen by this library**. They are the ESC/POS protocol itself — the bytes a thermal printer actually expects, defined by the standard and shared by every ESC/POS library regardless of language. That is why they are safe to hardcode here instead of referencing the underlying library&#039;s constants: they would not change even if the underlying library were replaced.

## Enum vs. constant

- **Enums** (`Derafu\Escpos\Enum\*`) are used when the values are a mutually exclusive choice — you pick exactly one (e.g. left, center or right justification).
- **Constants** (`Derafu\Escpos\Constant\*`) are used when the values are independent bits meant to be combined with `|` (e.g. font + emphasized + double width at the same time). An enum would force unwrapping `-&gt;value` on every term just to combine them, and the combined result would no longer be the enum type anyway — a plain `int` constant says what it is more honestly.

## Enums

| Enum | Cases | Used by |
|---|---|---|
| `Justification` | `Left`, `Center`, `Right` | `EscposPrinter::setJustification()` |
| `BarcodeType` | `Upca`, `Upce`, `Jan13`, `Jan8`, `Code39`, `Itf`, `Codabar`, `Code93`, `Code128` | `EscposPrinter::barcode()` |
| `Pdf417Option` | `Standard`, `Truncated` | `EscposPrinter::pdf417()` |
| `BarcodeTextPosition` | `None`, `Above`, `Below` | *(vocabulary only — no method exposes it yet)* |
| `Font` | `A`, `B`, `C` | *(vocabulary only)* |
| `Color` | `Color1`, `Color2` | *(vocabulary only)* |
| `CutMode` | `Full`, `Partial` | *(vocabulary only)* |
| `Underline` | `None`, `Single`, `Double` | *(vocabulary only)* |
| `QrErrorCorrectionLevel` | `L`, `M`, `Q`, `H` | *(vocabulary only)* |
| `QrModel` | `Model1`, `Model2`, `Micro` | *(vocabulary only)* |

The &quot;vocabulary only&quot; enums exist because they are a small, closed set defined by the protocol — completing them costs nothing and keeps this package generic instead of shaped only around what its first consumer needed. They are ready for the day a `setFont()`, `setColor()`, `setUnderline()`, `setBarcodeTextPosition()` or `qrCode()` method is added to `EscposPrinter`, without needing new vocabulary at that point.

```php
use Derafu\Escpos\Enum\Justification;

$printer-&gt;setJustification(Justification::Center);
```

## Constants (bitmask flags)

| Class | Constants | Used by |
|---|---|---|
| `PrintMode` | `FONT_A`, `FONT_B`, `EMPHASIZED`, `DOUBLE_HEIGHT`, `DOUBLE_WIDTH`, `UNDERLINE` | `EscposPrinter::selectPrintMode()` |
| `ImageSize` | `STANDARD`, `DOUBLE_WIDTH`, `DOUBLE_HEIGHT` | `EscposPrinter::image()` |

```php
use Derafu\Escpos\Constant\PrintMode;

// Combine flags with a plain bitwise OR.
$printer-&gt;selectPrintMode(PrintMode::FONT_B | PrintMode::EMPHASIZED);
```

## What is deliberately not vocabulary

A few things found in `mike42/escpos-php` are **not** mapped, on purpose:

- **Status request codes and raw control bytes** (`STATUS_*`, `ESC`, `GS`, `LF`, ...): these are not parameters of any command a caller chooses — they are either internal bytes the underlying library uses to build each command, or (for `STATUS_*`) not used by any method in the vendored version at all. Mapping them would be vocabulary for a feature that does not exist.
- **Character tables / code pages**: unlike everything above, the correct table number is not a fixed protocol value — it depends on the specific printer&#039;s capability profile. This is a known open item, not yet designed.




---

## Printing Images

Printing Images

# Printing Images

`EscposPrinter::image()` never asks for a type from the underlying ESC/POS library — it takes `Derafu\Escpos\ValueObject\RasterImage`, a small wrapper around a GD image (`\GdImage`). You decide how the pixels were produced; this package only knows how to print them.

## From an existing GD resource

```php
use Derafu\Escpos\ValueObject\RasterImage;

$gd = imagecreatefrompng(&#039;/path/to/logo.png&#039;);

$printer-&gt;image(new RasterImage($gd));
```

## From raw image bytes

If you already have PNG bytes (downloaded, generated, read from a database, etc.) instead of a GD resource, decode them with the named constructor:

```php
use Derafu\Escpos\ValueObject\RasterImage;

$logo = RasterImage::fromPngData(file_get_contents(&#039;/path/to/logo.png&#039;));

$printer-&gt;image($logo);
```

`fromPngData()` throws `Derafu\Escpos\Exception\InvalidImageDataException` if the given bytes cannot be decoded as an image — it never returns a broken `RasterImage`.

## PDF417 as a bitmap fallback

`EscposPrinter::pdf417()` sends the printer&#039;s native PDF417 command. Some cheaper thermal printers do not support it. For those, `Derafu\Escpos\Renderer\Pdf417Renderer` renders a PDF417 code as a raster image (using [`tecnickcom/tc-lib-barcode`](https://github.com/tecnickcom/tc-lib-barcode), a required dependency) that you then print like any other image:

```php
use Derafu\Escpos\Renderer\Pdf417Renderer;

$renderer = new Pdf417Renderer();
$image = $renderer-&gt;render(&#039;some-content-to-encode&#039;);

$printer-&gt;image($image);
```

This is a rendering concern, not a printing one, so it lives outside `EscposPrinter` on purpose: `EscposPrinter` has no idea `Pdf417Renderer` exists, and never will need to. You only reach for it when the native command does not work on your printer.




---

## Network Delivery

Network Delivery

# Network Delivery

`EscposPrinter::end()`/`dump()` return a plain string of ESC/POS bytes. What to do with that string — save it to a file, return it from an API, or send it to a real printer — is up to you, using whatever your application already uses for that (file writes, HTTP responses, etc.). This package does not wrap trivial I/O like `file_put_contents()`.

Delivering bytes to a network thermal printer over TCP is a different matter: it needs connection timeout handling and clear error reporting, which is easy to get subtly wrong by hand (and it is the standard delivery mechanism for ESC/POS printers, not a generic file/API concern). `Derafu\Escpos\Transport\NetworkTransport` covers exactly that, with plain PHP streams — it has no dependency on `mike42/escpos-php` or any other ESC/POS library.

```php
use Derafu\Escpos\Transport\NetworkTransport;

$escposData = $printer-&gt;end();

(new NetworkTransport())-&gt;send($escposData, &#039;172.16.1.5&#039;);
// Port defaults to NetworkTransport::DEFAULT_PORT (9100, the de facto
// standard raw ESC/POS printing port). Pass a third argument to override
// it, and a fourth to change the connection timeout (default 5 seconds).
```

If the connection fails, or not all bytes could be delivered, it throws `Derafu\Escpos\Exception\NetworkDeliveryException` with the real connection error — never a silently swallowed failure.

```php
use Derafu\Escpos\Exception\NetworkDeliveryException;
use Derafu\Escpos\Transport\NetworkTransport;

try {
    (new NetworkTransport())-&gt;send($escposData, &#039;172.16.1.5&#039;);
} catch (NetworkDeliveryException $e) {
    // $e-&gt;getMessage() includes the host, port, and the underlying error.
}
```

## `bin/print.php`

The repository also has `bin/print.php`, a thin CLI wrapper around `NetworkTransport` used to quickly validate ESC/POS output against a real printer during development:

```shell
bin/print.php 172.16.1.5:9100 ticket.escpos
cat ticket.escpos | bin/print.php 172.16.1.5
php examples/full-example.php | bin/print.php 172.16.1.5
```

This is an internal development tool, not part of the package&#039;s public API: it is not registered as a Composer binary, so it will not appear in `vendor/bin/` when you require this package. It still physically exists inside the installed package, at `vendor/derafu/escpos/bin/print.php`, so it can be run from there if you know the path — but it is not documented or supported as something to build on. Use `NetworkTransport` directly in your own code instead.




---

## Architecture

Architecture

# Architecture

`derafu/escpos` wraps [mike42/escpos-php](https://github.com/mike42/escpos-php) instead of implementing the ESC/POS protocol itself — but that library is treated as a replaceable implementation detail, not part of this package&#039;s contract. Nothing in the public API accepts or returns a `Mike42\Escpos\*` type or constant; see [Vocabulary](./vocabulary) and [Printing Images](./images) for the two places that leak would normally happen (command values, and image types) and how they are closed.

## Why this matters

If a consumer&#039;s code imports `Mike42\Escpos\*` directly to call this package, upgrading `mike42/escpos-php` to a new major version can break that consumer&#039;s code too — even though they never touched the underlying library&#039;s version themselves. Isolating it behind this package&#039;s own API means a breaking change in the underlying library only needs to be absorbed in one place: here.

## The seam: `PrinterInterface`

`Derafu\Escpos\Contract\PrinterInterface` defines every operation `EscposPrinter` needs from a backend: `text()`, `feed()`, `selectPrintMode()`, `setJustification()`, `barcode()`, `pdf417()`, `image()`, `cut()`, `pulse()`, `getOutput()`, `close()`.

`Derafu\Escpos\Adapter\Mike42Printer` is the only class in this package allowed to `use Mike42\Escpos\*`, and it is the default implementation of `PrinterInterface`. `EscposPrinter` is a thin facade: it owns option resolution, the `sprintf()`/line-break convenience of `print()`/`println()`, special character handling, and the `end()` workflow (finish, optionally cut and pulse) — and delegates everything else to whichever `PrinterInterface` it was given.

```php
use Derafu\Escpos\Contract\PrinterInterface;
use Derafu\Escpos\EscposPrinter;

// Normally you never need this — omitting the second argument builds the
// default Mike42Printer from your options array.
$printer = new EscposPrinter($options, $customPrinterImplementation);
```

That second constructor argument is the extension point: a different backend (or a test double) can be swapped in without changing `EscposPrinter`&#039;s public API, or any code that already depends on `PrinterInterface` instead of the concrete class.

## Exceptions

Every exception this package throws implements `Derafu\Escpos\Exception\EscposExceptionInterface`, so you can catch anything it raises without needing to know the specific class:

| Exception | Thrown when |
|---|---|
| `ConnectorNotSupportedException` | The configured connector type has no known implementation (only `memory` exists today). |
| `ProfileNotFoundException` | The configured capability profile does not exist. |
| `UnbufferedConnectorException` | `dump()`/`end()` is called on a connector that does not buffer data in memory (`memory` always does; this exists so the failure mode is a clear exception if that ever changes, not a corrupted read). |
| `InvalidImageDataException` | `RasterImage::fromPngData()` is given data that cannot be decoded as an image. |
| `NetworkDeliveryException` | `NetworkTransport::send()` cannot connect, or cannot deliver all bytes. |

None of these extend a `Mike42\Escpos\*` exception, for the same reason described above.





---
Last updated on 09/09/2026
#php
