TRMNL Pipeline PHP provides a streamlined API, based on the pipeline pattern, for converting HTML content (or images) into optimized images for e-ink devices supported by the TRMNL Models API. The image processing pipeline includes features like scaling, rotation, grayscale conversion, color quantization, and format-specific optimizations. This package is used in usetrmnl/byos_laravel.
Command line wrapper for this package: trmnl-pipeline-cmd
- Browser Rendering: HTML to image conversion using Spatie Browsershot
- Image Processing: Advanced image manipulation using ImageMagick
- TRMNL Models API: Automatic support for >=12 different e-ink device models.
- PHP 8.2 or higher (PHP 8.4 + 8.5 are tested in CI)
- Imagick extension
- Spatie Browsershot (requires Node.js and Puppeteer -> see Browsershot Requirements)
You can install the package via composer:
composer require bnussbau/trmnl-pipeline-phpRender HTML and convert to image compatible with the TRMNL OG model.
use Bnussbau\TrmnlPipeline\Model;
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
use Bnussbau\TrmnlPipeline\Stages\ImageStage;
use Bnussbau\TrmnlPipeline\Stages\BrowserStage;
$html = file_get_contents('./tests/assets/framework2_og.html');
$image = new TrmnlPipeline()
->model(Model::OG)
->pipe(new BrowserStage()
->html($html))
->pipe(new ImageStage())
->process();
echo "Generated image: $image";Generates PNG 800x480 8-bit Grayscale Gray 4c
use Bnussbau\TrmnlPipeline\Stages\ImageStage;
use Bnussbau\TrmnlPipeline\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
use Bnussbau\TrmnlPipeline\Model;
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
use Bnussbau\TrmnlPipeline\Stages\ImageStage;
use Bnussbau\TrmnlPipeline\Stages\BrowserStage;
$html = file_get_contents('./tests/assets/framework2_og.html');
$image = new TrmnlPipeline()
->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";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\TrmnlPipeline\Stages\BrowserStage;
$image = (new \Bnussbau\TrmnlPipeline\TrmnlPipeline())
->pipe((new BrowserStage())
->timezone('America/New_York')
->html('<html><body><script>document.write(new Date().toString())</script></body></html>'))
->pipe(new \Bnussbau\TrmnlPipeline\Stages\ImageStage())
->process();You can use different Browsershot implementations (like BrowsershotLambda) by passing an instance to the BrowserStage. See installation instructions and requirments for stefanzweifel/sidecar-browsershot.
use Bnussbau\TrmnlPipeline\Model;
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
use Bnussbau\TrmnlPipeline\Stages\BrowserStage;
use Bnussbau\TrmnlPipeline\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 TrmnlPipeline()
->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.
You can use the fake() method to prevent actual Browsershot and Imagick operations:
use Bnussbau\TrmnlPipeline\Model;
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
use Bnussbau\TrmnlPipeline\Stages\BrowserStage;
use Bnussbau\TrmnlPipeline\Stages\ImageStage;
// Enable fake mode for testing
TrmnlPipeline::fake();
$html = '<html><body>Test Content</body></html>';
$result = (new TrmnlPipeline())
->model(Model::OG)
->pipe(new BrowserStage()->html($html))
->pipe(new ImageStage())
->process();
echo "Mock image generated: $result";
// Disable fake mode when done
TrmnlPipeline::restore();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 payloadConverts 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()onImageStageso 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('<html><body>Content</body></html>')
->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 TrmnlPipeline())
->model(Model::OG)
->pipe((new BrowserStage())->url('https://example.com'))
->pipe((new ImageStage())->dither())
->process();Processes images for e-ink 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');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\TrmnlPipeline\Stages\ImageStage;
(new ImageStage())
->dither()
->colors(2)
->bitDepth(1);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\TrmnlPipeline\Model;
use Bnussbau\TrmnlPipeline\TrmnlPipeline;
use Bnussbau\TrmnlPipeline\Stages\BrowserStage;
use Bnussbau\TrmnlPipeline\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 TrmnlPipeline()
->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\TrmnlPipeline\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";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(); // 1composer test
composer test-coveragecomposer format
composer analyse
composer rectorMIT License. See LICENSE file for details.
- Create an issue to discuss your idea
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests to maintain coverage
- Run the test suite
- Submit a pull request