PHPackages                             bnussbau/trmnl-pipeline-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Image &amp; Media](/categories/media)
4. /
5. bnussbau/trmnl-pipeline-php

ActiveLibrary[Image &amp; Media](/categories/media)

bnussbau/trmnl-pipeline-php
===========================

Convert HTML content into optimized images for a range of e-paper devices.

1.0.0(1mo ago)02.7k↓48%4[1 issues](https://github.com/bnussbau/epaper-pipeline-php/issues)[1 PRs](https://github.com/bnussbau/epaper-pipeline-php/pulls)MITPHPPHP ^8.2CI passing

Since Sep 18Pushed 1mo agoCompare

[ Source](https://github.com/bnussbau/epaper-pipeline-php)[ Packagist](https://packagist.org/packages/bnussbau/trmnl-pipeline-php)[ Docs](https://github.com/bnussbau/epaper-pipeline-php)[ Fund](https://www.buymeacoffee.com/bnussbau)[ Fund](https://trmnl.com/?ref=laravel-trmnl)[ RSS](/packages/bnussbau-trmnl-pipeline-php/feed)WikiDiscussions main Synced today

READMEChangelog (10)Dependencies (12)Versions (16)Used By (0)

ePaper Pipeline PHP
===================

[](#epaper-pipeline-php)

[![Latest Version on Packagist](https://camo.githubusercontent.com/7fac5d0ac2d74b3751ed4426b946fa9f1d0515866218f3762d2f9ef6f745738b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f626e7573736261752f6570617065722d706970656c696e652d7068702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bnussbau/trmnl-pipeline-php)[![License](https://camo.githubusercontent.com/f17af845a1e5bad11c971beba5672e88852da6215d493615c3c41a967c7ddc01/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652532302d4d49542d626c75653f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bnussbau/trmnl-pipeline-php)[![Tests](https://camo.githubusercontent.com/c68f29c593006dd3bec847e14987f20a130b80515f20e24dad7044176ed1fd32/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f626e7573736261752f74726d6e6c2d706970656c696e652d7068702f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/bnussbau/trmnl-pipeline-php/actions/workflows/run-tests.yml)[![Total Downloads](https://camo.githubusercontent.com/c07b1a19d2d17c580f986440b1a1f2949d666395dfc70200e661ba25952d128e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f626e7573736261752f74726d6e6c2d706970656c696e652d7068702e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bnussbau/trmnl-pipeline-php)

ePaper Pipeline PHP provides a streamlined API, based on the pipeline pattern, for converting HTML content (or images) into optimized images for e-Paper devices. The image processing pipeline includes features like scaling, rotation, grayscale conversion, color quantization, and format-specific optimizations. This package is used in [usetrmnl/larapaper](https://github.com/usetrmnl/larapaper). Devices model presets are provided by the [TRMNL Models API](https://trmnl.com/api/models).

Command line wrapper for this package: [epaper-pipeline-cmd](https://github.com/bnussbau/epaper-pipeline-cmd)

[![browsershot_cham2v3cji4nbZ5JXN6_processed](https://private-user-images.githubusercontent.com/8931007/586246652-0bb7aa88-3854-4e4b-a67d-3980196027b1.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODI2OTExNTIsIm5iZiI6MTc4MjY5MDg1MiwicGF0aCI6Ii84OTMxMDA3LzU4NjI0NjY1Mi0wYmI3YWE4OC0zODU0LTRlNGItYTY3ZC0zOTgwMTk2MDI3YjEucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI2MDYyOCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNjA2MjhUMjM1NDEyWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZjkxZWZkYjkzMjBlZTNkOWIxZWZjOWI4NWM4N2I3YzkxNTYwMTgzMzRjZDZjYmFlYmE3MjE0NDZiMzdmYWZiZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmcmVzcG9uc2UtY29udGVudC10eXBlPWltYWdlJTJGcG5nIn0.nr0xet726TgJ66Fg4Lw5VXoudegOGzZrG7664QT3sw0)](https://private-user-images.githubusercontent.com/8931007/586246652-0bb7aa88-3854-4e4b-a67d-3980196027b1.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODI2OTExNTIsIm5iZiI6MTc4MjY5MDg1MiwicGF0aCI6Ii84OTMxMDA3LzU4NjI0NjY1Mi0wYmI3YWE4OC0zODU0LTRlNGItYTY3ZC0zOTgwMTk2MDI3YjEucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI2MDYyOCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNjA2MjhUMjM1NDEyWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9ZjkxZWZkYjkzMjBlZTNkOWIxZWZjOWI4NWM4N2I3YzkxNTYwMTgzMzRjZDZjYmFlYmE3MjE0NDZiMzdmYWZiZCZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmcmVzcG9uc2UtY29udGVudC10eXBlPWltYWdlJTJGcG5nIn0.nr0xet726TgJ66Fg4Lw5VXoudegOGzZrG7664QT3sw0)Features
--------

[](#features)

- **Browser Rendering**: HTML to image conversion using Spatie Browsershot
- **Image Processing**: Advanced image manipulation using ImageMagick
- **TRMNL Models API**: Automatic support for &gt;=18 different e-paper device models (TRMNL, Seeed Studio, Kobo, Kindle, Nook, …).

Requirements
------------

[](#requirements)

- PHP 8.2 or higher (PHP 8.4 + 8.5 are tested in CI)
- Imagick extension
- Spatie Browsershot (requires Node.js and Puppeteer -&gt; see [Browsershot Requirements](https://spatie.be/docs/browsershot/v4/requirements))

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require bnussbau/epaper-pipeline-php
```

Usage
-----

[](#usage)

### With Model Configuration

[](#with-model-configuration)

Render HTML and convert to image compatible with the TRMNL OG model.

```
use Bnussbau\EpaperPipeline\Model;
use Bnussbau\EpaperPipeline\EpaperPipeline;
use Bnussbau\EpaperPipeline\Stages\ImageStage;
use Bnussbau\EpaperPipeline\Stages\BrowserStage;

$html = file_get_contents('./tests/assets/framework2_og.html');

$image = new EpaperPipeline()
    ->model(Model::OG)
    ->pipe(new BrowserStage()
        ->html($html))
    ->pipe(new ImageStage())
    ->process();

echo "Generated image: $image";
```

Generates PNG 800x480 8-bit Grayscale Gray 4c

### Image Processing Only

[](#image-processing-only)

```
use Bnussbau\EpaperPipeline\Stages\ImageStage;
use Bnussbau\EpaperPipeline\Model;

$imageStage = new ImageStage();
$imageStage->configureFromModel(Model::OG_BMP);

$result = $imageStage('./tests/assets/browsershot_og_1bit.png');
echo "Processed image: $result";
```

Generates BMP3 800x480 1-bit sRGB 2c

### Manual Configuration

[](#manual-configuration)

```
use Bnussbau\EpaperPipeline\Model;
use Bnussbau\EpaperPipeline\EpaperPipeline;
use Bnussbau\EpaperPipeline\Stages\ImageStage;
use Bnussbau\EpaperPipeline\Stages\BrowserStage;

$html = file_get_contents('./tests/assets/framework2_og.html');

$image = new EpaperPipeline()
    ->pipe(new BrowserStage()
        ->html($html))
    ->pipe(new ImageStage()
        ->format('png')
        ->width(800)
        ->height(600)
        ->rotation(90)
        ->colors(256)
        ->bitDepth(8))
    ->process();

echo "Generated image: $image";
```

### Setting the Browser Timezone (optional)

[](#setting-the-browser-timezone-optional)

You can control the timezone used by the headless browser when rendering your HTML by calling `timezone()` on `BrowserStage` with any valid PHP timezone identifier (e.g., `UTC`, `America/New_York`, `Europe/Berlin`).

Notes:

- The timezone is only applied when you explicitly call `timezone()`. If you don’t explicitly set the timezone, the browser will use the system timezone.
- This can be helpful when your HTML or scripts render time/date-dependent content.

Example:

```
use Bnussbau\EpaperPipeline\Stages\BrowserStage;

$image = (new \Bnussbau\EpaperPipeline\EpaperPipeline())
    ->pipe((new BrowserStage())
        ->timezone('America/New_York')
        ->html('document.write(new Date().toString())'))
    ->pipe(new \Bnussbau\EpaperPipeline\Stages\ImageStage())
    ->process();
```

### Browser Rendering on AWS Lambda

[](#browser-rendering-on-aws-lambda)

You can use different Browsershot implementations (like BrowsershotLambda) by passing an instance to the BrowserStage. See installation instructions and requirments for [stefanzweifel/sidecar-browsershot](https://github.com/stefanzweifel/sidecar-browsershot).

```
use Bnussbau\EpaperPipeline\Model;
use Bnussbau\EpaperPipeline\EpaperPipeline;
use Bnussbau\EpaperPipeline\Stages\BrowserStage;
use Bnussbau\EpaperPipeline\Stages\ImageStage;
use Wnx\SidecarBrowsershot\BrowsershotLambda;

$html = file_get_contents('./tests/assets/framework2_og.html');

// Create your custom Browsershot instance (e.g., BrowsershotLambda)
$browsershotLambda = new BrowsershotLambda();

$image = new EpaperPipeline()
    ->model(Model::OG)
    ->pipe(new BrowserStage($browsershotLambda)
        ->html($html))
    ->pipe(new ImageStage())
    ->process();

echo "Generated image: $image";
```

This allows you to use BrowsershotLambda or any other Browsershot implementation that extends `Spatie\Browsershot\Browsershot`.

### Testing with Fake Mode

[](#testing-with-fake-mode)

You can use the `fake()` method to prevent actual Browsershot and Imagick operations:

```
use Bnussbau\EpaperPipeline\Model;
use Bnussbau\EpaperPipeline\EpaperPipeline;
use Bnussbau\EpaperPipeline\Stages\BrowserStage;
use Bnussbau\EpaperPipeline\Stages\ImageStage;

// Enable fake mode for testing
EpaperPipeline::fake();

$html = 'Test Content';

$result = (new EpaperPipeline())
    ->model(Model::OG)
    ->pipe(new BrowserStage()->html($html))
    ->pipe(new ImageStage())
    ->process();

echo "Mock image generated: $result";

// Disable fake mode when done
EpaperPipeline::restore();
```

API Reference
-------------

[](#api-reference)

### Pipeline

[](#pipeline)

The main pipeline class that orchestrates the processing stages.

```
$pipeline = new Pipeline();
$pipeline->model(Model::OG_PNG); // Set model for automatic configuration
$pipeline->pipe(new BrowserStage()); // Add browser stage
$pipeline->pipe(new ImageStage()); // Add image stage
$result = $pipeline->process($payload); // Process payload
```

### BrowserStage

[](#browserstage)

Converts HTML or a URL to PNG images using Spatie Browsershot. You must provide content via either `html()` or `url()` (mutually exclusive).

- **`html(string $html)`** — Set HTML content to render (e.g. a template or string).
- **`url(string $url)`** — Set a URL to capture (e.g. `https://example.com`). Use this when you want to screenshot a live webpage instead of rendering HTML. If the page is not black-and-white or contains photos, combine with **`->dither()`** on `ImageStage` so the image is converted cleanly to the model’s limited palette; otherwise gradients and images can look harsh or banded.

```
$browserStage = new BrowserStage();
$browserStage
    ->html('Content')
    ->width(800)
    ->height(480)
    ->useDefaultDimensions() // force 800x480 e.g. in combination with Model to upscale image
    ->setBrowsershotOption('addStyleTag', json_encode(['content' => 'body{ color: red; }']));

$result = $browserStage(null);
```

Screenshot from URL with dithering (recommended for full-color or image-heavy pages):

```
$image = (new EpaperPipeline())
    ->model(Model::OG)
    ->pipe((new BrowserStage())->url('https://example.com'))
    ->pipe((new ImageStage())->dither())
    ->process();
```

### ImageStage

[](#imagestage)

Processes images for e-paper display compatibility.

```
$imageStage = new ImageStage();
$imageStage
    ->format('png')
    ->width(800)
    ->height(480)
    ->offsetX(0)
    ->offsetY(0)
    ->rotation(0)
    ->colors(2)
    ->bitDepth(1)
    ->outputPath('/path/to/output.png');

$result = $imageStage('/path/to/input.png');
```

#### Dithering

[](#dithering)

Recommended for photos but not for images containing mostly text, where it can make edges and letters appear rough or unclear. Dithering converts a grayscale photo into only black and white pixels by using patterns or noise to simulate intermediate shades, creating the illusion of continuous tones through spatial averaging.

```
use Bnussbau\EpaperPipeline\Stages\ImageStage;

(new ImageStage())
    ->dither()
    ->colors(2)
    ->bitDepth(1);
```

#### Color Support

[](#color-support)

The pipeline supports color images via palettes defined in `palettes.json`. Models can specify one or more palette IDs, and the first palette with a `colors` array will be automatically applied. Color palettes use RGB colorspace for quantization and support dithering.

**Example 1: Using Model Preset with Color Palette**

```
use Bnussbau\EpaperPipeline\Model;
use Bnussbau\EpaperPipeline\EpaperPipeline;
use Bnussbau\EpaperPipeline\Stages\BrowserStage;
use Bnussbau\EpaperPipeline\Stages\ImageStage;

$html = file_get_contents('./tests/assets/color_6a_test.html');

// Inky Impression 13.3 model has color-6a palette (6 colors: red, green, blue, yellow, black, white)
$image = new EpaperPipeline()
    ->model(Model::INKY_IMPRESSION_13_3)
    ->pipe(new BrowserStage()
        ->html($html))
    ->pipe(new ImageStage())
    ->process();

echo "Generated color image: $image";
```

**Example 2: Defining Color Palette as Array**

```
use Bnussbau\EpaperPipeline\Stages\ImageStage;

// Define custom color palette (6 colors)
$colorPalette = [
    '#FF0000', // Red
    '#00FF00', // Green
    '#0000FF', // Blue
    '#FFFF00', // Yellow
    '#000000', // Black
    '#FFFFFF', // White
];

$imageStage = new ImageStage();
$imageStage
    ->format('png')
    ->colormap($colorPalette)
    ->dither(true); // Dithering works with color palettes (optional; only use for images)

$result = $imageStage('/path/to/input.png');
echo "Processed color image: $result";
```

### Model

[](#model)

Access device model configurations.

```
$model = Model::OG_PNG;
$data = $model->getData();

echo $model->getLabel(); // "TRMNL OG (1-bit)"
echo $model->getWidth(); // 800
echo $model->getHeight(); // 480
echo $model->getColors(); // 2
echo $model->getBitDepth(); // 1
```

Development
-----------

[](#development)

### Running Tests

[](#running-tests)

```
composer test
composer test-coverage
```

### Code Quality

[](#code-quality)

```
composer format
composer analyse
composer rector
```

License
-------

[](#license)

MIT License. See LICENSE file for details.

Contributing
------------

[](#contributing)

1. Create an issue to discuss your idea
2. Fork the repository
3. Create a feature branch
4. Make your changes
5. Add tests to maintain coverage
6. Run the test suite
7. Submit a pull request

###  Health Score

48

—

FairBetter than 93% of packages

Maintenance90

Actively maintained with recent releases

Popularity23

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 86.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~21 days

Recently: every ~41 days

Total

12

Last Release

52d ago

Major Versions

0.8.0 → 1.0.02026-05-12

### Community

Maintainers

![](https://www.gravatar.com/avatar/53653f306f20e3e075f9d9243a1daa101ae2f0f7fbd82329793e8e5acba6afba?d=identicon)[bnussbau](/maintainers/bnussbau)

---

Top Contributors

[![bnussbau](https://avatars.githubusercontent.com/u/8931007?v=4)](https://github.com/bnussbau "bnussbau (59 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (7 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (2 commits)")

---

Tags

epaperphppipelinebnussbauepaperepaper-pipeline-phpe-paper

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/bnussbau-trmnl-pipeline-php/health.svg)

```
[![Health](https://phpackages.com/badges/bnussbau-trmnl-pipeline-php/health.svg)](https://phpackages.com/packages/bnussbau-trmnl-pipeline-php)
```

###  Alternatives

[wnx/sidecar-browsershot

A Sidecar function to run Browsershot on Lambda.

2351.8M10](/packages/wnx-sidecar-browsershot)[goat1000/svggraph

Generates SVG graphs

135911.1k3](/packages/goat1000-svggraph)[concrete5/core

Concrete core subtree split

20166.1k52](/packages/concrete5-core)[gravatarphp/gravatar

Gravatar URL builder which is most commonly called as a Gravatar library

16653.6k2](/packages/gravatarphp-gravatar)[tapp/filament-lms

193.4k](/packages/tapp-filament-lms)[glennraya/xendivel

A Laravel package to easily integrate Xendit payment gateway. It supports credit and debit cards, and e-wallet payments and custom invoices, queued notifications, webhook listeners and more.

442.7k](/packages/glennraya-xendivel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
