{"id":31642521,"url":"https://github.com/bnussbau/trmnl-pipeline-php","last_synced_at":"2026-01-20T16:29:52.403Z","repository":{"id":315320525,"uuid":"1059015912","full_name":"bnussbau/trmnl-pipeline-php","owner":"bnussbau","description":"Convert HTML content into optimized images for a range of e-ink devices.","archived":false,"fork":false,"pushed_at":"2025-11-25T17:03:57.000Z","size":81,"stargazers_count":0,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-28T22:17:48.006Z","etag":null,"topics":["trmnl","trmnlserver"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bnussbau.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"bnussbau","buy_me_a_coffee":"bnussbau","custom":["https://usetrmnl.com/?ref=laravel-trmnl"]}},"created_at":"2025-09-17T21:50:14.000Z","updated_at":"2025-11-25T17:04:00.000Z","dependencies_parsed_at":"2025-09-18T00:23:44.947Z","dependency_job_id":"ba12e816-2910-401b-bb20-8d9b4a5b30e7","html_url":"https://github.com/bnussbau/trmnl-pipeline-php","commit_stats":null,"previous_names":["bnussbau/trmnl-pipeline-php"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/bnussbau/trmnl-pipeline-php","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnussbau%2Ftrmnl-pipeline-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnussbau%2Ftrmnl-pipeline-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnussbau%2Ftrmnl-pipeline-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnussbau%2Ftrmnl-pipeline-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bnussbau","download_url":"https://codeload.github.com/bnussbau/trmnl-pipeline-php/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bnussbau%2Ftrmnl-pipeline-php/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28607159,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T16:10:39.856Z","status":"ssl_error","status_checked_at":"2026-01-20T16:10:39.493Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["trmnl","trmnlserver"],"created_at":"2025-10-07T04:00:06.228Z","updated_at":"2026-01-20T16:29:52.397Z","avatar_url":"https://github.com/bnussbau.png","language":"PHP","funding_links":["https://github.com/sponsors/bnussbau","https://buymeacoffee.com/bnussbau","https://usetrmnl.com/?ref=laravel-trmnl"],"categories":[],"sub_categories":[],"readme":"# TRMNL Pipeline PHP\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/bnussbau/trmnl-pipeline-php.svg?style=flat-square)](https://packagist.org/packages/bnussbau/trmnl-pipeline-php)\n[![License](https://img.shields.io/badge/License%20-MIT-blue?style=flat-square)](https://packagist.org/packages/bnussbau/trmnl-pipeline-php)\n[![Tests](https://img.shields.io/github/actions/workflow/status/bnussbau/trmnl-pipeline-php/run-tests.yml?branch=main\u0026label=tests\u0026style=flat-square)](https://github.com/bnussbau/trmnl-pipeline-php/actions/workflows/run-tests.yml)\n[![Total Downloads](https://img.shields.io/packagist/dt/bnussbau/trmnl-pipeline-php.svg?style=flat-square)](https://packagist.org/packages/bnussbau/trmnl-pipeline-php)\n\nTRMNL 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](https://usetrmnl.com/api/models). 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](https://github.com/usetrmnl/byos_laravel).\n\nCommand line wrapper for this package: [trmnl-pipeline-cmd](https://github.com/bnussbau/trmnl-pipeline-cmd)\n\n\u003cimg width=\"800\" height=\"480\" alt=\"image\" src=\"https://github.com/user-attachments/assets/e84fc752-552e-4cb9-a1c0-aa2596176db7\" /\u003e\n\n\n## Features\n\n- **Browser Rendering**: HTML to image conversion using Spatie Browsershot\n- **Image Processing**: Advanced image manipulation using ImageMagick\n- **TRMNL Models API**: Automatic support for \u003e=12 different e-ink device models.\n\n## Requirements\n\n- PHP 8.2 or higher\n- Imagick extension\n- Spatie Browsershot (requires Node.js and Puppeteer -\u003e see [Browsershot Requirements](https://spatie.be/docs/browsershot/v4/requirements))\n\n## Installation\nYou can install the package via composer:\n\n```bash\ncomposer require bnussbau/trmnl-pipeline-php\n```\n\n## Usage\n\n### With Model Configuration\nRender HTML and convert to image compatible with the TRMNL OG model.\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Model;\nuse Bnussbau\\TrmnlPipeline\\TrmnlPipeline;\nuse Bnussbau\\TrmnlPipeline\\Stages\\ImageStage;\nuse Bnussbau\\TrmnlPipeline\\Stages\\BrowserStage;\n\n$html = file_get_contents('./tests/assets/framework2_og.html');\n\n$image = new TrmnlPipeline()\n    -\u003emodel(Model::OG)\n    -\u003epipe(new BrowserStage()\n        -\u003ehtml($html))\n    -\u003epipe(new ImageStage())\n    -\u003eprocess();\n\necho \"Generated image: $image\";\n```\nGenerates PNG 800x480 8-bit Grayscale Gray 4c\n\n### Image Processing Only\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Stages\\ImageStage;\nuse Bnussbau\\TrmnlPipeline\\Model;\n\n$imageStage = new ImageStage();\n$imageStage-\u003econfigureFromModel(Model::OG_BMP);\n\n$result = $imageStage('./tests/assets/browsershot_og_1bit.png');\necho \"Processed image: $result\";\n```\n\nGenerates BMP3 800x480 1-bit sRGB 2c\n\n### Manual Configuration\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Model;\nuse Bnussbau\\TrmnlPipeline\\TrmnlPipeline;\nuse Bnussbau\\TrmnlPipeline\\Stages\\ImageStage;\nuse Bnussbau\\TrmnlPipeline\\Stages\\BrowserStage;\n\n$html = file_get_contents('./tests/assets/framework2_og.html');\n\n$image = new TrmnlPipeline()\n    -\u003epipe(new BrowserStage()\n        -\u003ehtml($html))\n    -\u003epipe(new ImageStage()\n        -\u003eformat('png')\n        -\u003ewidth(800)\n        -\u003eheight(600)\n        -\u003erotation(90)\n        -\u003ecolors(256)\n        -\u003ebitDepth(8))\n    -\u003eprocess();\n\necho \"Generated image: $image\";\n```\n\n### Setting the Browser Timezone (optional)\n\nYou 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`).\n\nNotes:\n- 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.\n- This can be helpful when your HTML or scripts render time/date-dependent content.\n\nExample:\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Stages\\BrowserStage;\n\n$image = (new \\Bnussbau\\TrmnlPipeline\\TrmnlPipeline())\n    -\u003epipe((new BrowserStage())\n        -\u003etimezone('America/New_York')\n        -\u003ehtml('\u003chtml\u003e\u003cbody\u003e\u003cscript\u003edocument.write(new Date().toString())\u003c/script\u003e\u003c/body\u003e\u003c/html\u003e'))\n    -\u003epipe(new \\Bnussbau\\TrmnlPipeline\\Stages\\ImageStage())\n    -\u003eprocess();\n```\n\n### Browser Rendering on AWS Lambda\n\nYou can use different Browsershot implementations (like BrowsershotLambda) by passing an instance to the BrowserStage.\nSee installation instructions and requirments for [stefanzweifel/sidecar-browsershot](https://github.com/stefanzweifel/sidecar-browsershot).\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Model;\nuse Bnussbau\\TrmnlPipeline\\TrmnlPipeline;\nuse Bnussbau\\TrmnlPipeline\\Stages\\BrowserStage;\nuse Bnussbau\\TrmnlPipeline\\Stages\\ImageStage;\nuse Wnx\\SidecarBrowsershot\\BrowsershotLambda;\n\n$html = file_get_contents('./tests/assets/framework2_og.html');\n\n// Create your custom Browsershot instance (e.g., BrowsershotLambda)\n$browsershotLambda = new BrowsershotLambda();\n\n$image = new TrmnlPipeline()\n    -\u003emodel(Model::OG)\n    -\u003epipe(new BrowserStage($browsershotLambda)\n        -\u003ehtml($html))\n    -\u003epipe(new ImageStage())\n    -\u003eprocess();\n\necho \"Generated image: $image\";\n```\n\nThis allows you to use BrowsershotLambda or any other Browsershot implementation that extends `Spatie\\Browsershot\\Browsershot`.\n\n### Testing with Fake Mode\n\nYou can use the `fake()` method to prevent actual Browsershot and Imagick operations:\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Model;\nuse Bnussbau\\TrmnlPipeline\\TrmnlPipeline;\nuse Bnussbau\\TrmnlPipeline\\Stages\\BrowserStage;\nuse Bnussbau\\TrmnlPipeline\\Stages\\ImageStage;\n\n// Enable fake mode for testing\nTrmnlPipeline::fake();\n\n$html = '\u003chtml\u003e\u003cbody\u003eTest Content\u003c/body\u003e\u003c/html\u003e';\n\n$result = (new TrmnlPipeline())\n    -\u003emodel(Model::OG)\n    -\u003epipe(new BrowserStage()-\u003ehtml($html))\n    -\u003epipe(new ImageStage())\n    -\u003eprocess();\n\necho \"Mock image generated: $result\";\n\n// Disable fake mode when done\nTrmnlPipeline::restore();\n```\n\n## API Reference\n\n### Pipeline\n\nThe main pipeline class that orchestrates the processing stages.\n\n```php\n$pipeline = new Pipeline();\n$pipeline-\u003emodel(Model::OG_PNG); // Set model for automatic configuration\n$pipeline-\u003epipe(new BrowserStage()); // Add browser stage\n$pipeline-\u003epipe(new ImageStage()); // Add image stage\n$result = $pipeline-\u003eprocess($payload); // Process payload\n```\n\n### BrowserStage\n\nConverts HTML to PNG images using Spatie Browsershot.\n\n```php\n$browserStage = new BrowserStage();\n$browserStage\n    -\u003ehtml('\u003chtml\u003e\u003cbody\u003eContent\u003c/body\u003e\u003c/html\u003e')\n    -\u003ewidth(800)\n    -\u003eheight(480)\n    -\u003euseDefaultDimensions() // force 800x480 e.g. in combination with Model to upscale image\n    -\u003esetBrowsershotOption('addStyleTag', json_encode(['content' =\u003e 'body{ color: red; }']));\n\n$result = $browserStage(null);\n```\n\n### ImageStage\n\nProcesses images for e-ink display compatibility.\n\n```php\n$imageStage = new ImageStage();\n$imageStage\n    -\u003eformat('png')\n    -\u003ewidth(800)\n    -\u003eheight(480)\n    -\u003eoffsetX(0)\n    -\u003eoffsetY(0)\n    -\u003erotation(0)\n    -\u003ecolors(2)\n    -\u003ebitDepth(1)\n    -\u003eoutputPath('/path/to/output.png');\n\n$result = $imageStage('/path/to/input.png');\n```\n\n#### Dithering\n\nRecommended 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.\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Stages\\ImageStage;\n\n(new ImageStage())\n    -\u003edither()\n    -\u003ecolors(2)\n    -\u003ebitDepth(1);\n```\n\n#### Color Support\n\nThe 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.\n\n**Example 1: Using Model Preset with Color Palette**\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Model;\nuse Bnussbau\\TrmnlPipeline\\TrmnlPipeline;\nuse Bnussbau\\TrmnlPipeline\\Stages\\BrowserStage;\nuse Bnussbau\\TrmnlPipeline\\Stages\\ImageStage;\n\n$html = file_get_contents('./tests/assets/color_6a_test.html');\n\n// Inky Impression 13.3 model has color-6a palette (6 colors: red, green, blue, yellow, black, white)\n$image = new TrmnlPipeline()\n    -\u003emodel(Model::INKY_IMPRESSION_13_3)\n    -\u003epipe(new BrowserStage()\n        -\u003ehtml($html))\n    -\u003epipe(new ImageStage())\n    -\u003eprocess();\n\necho \"Generated color image: $image\";\n```\n\n**Example 2: Defining Color Palette as Array**\n\n```php\nuse Bnussbau\\TrmnlPipeline\\Stages\\ImageStage;\n\n// Define custom color palette (6 colors)\n$colorPalette = [\n    '#FF0000', // Red\n    '#00FF00', // Green\n    '#0000FF', // Blue\n    '#FFFF00', // Yellow\n    '#000000', // Black\n    '#FFFFFF', // White\n];\n\n$imageStage = new ImageStage();\n$imageStage\n    -\u003eformat('png')\n    -\u003ecolormap($colorPalette)\n    -\u003edither(true); // Dithering works with color palettes (optional; only use for images)\n\n$result = $imageStage('/path/to/input.png');\necho \"Processed color image: $result\";\n```\n\n### Model\n\nAccess device model configurations.\n\n```php\n$model = Model::OG_PNG;\n$data = $model-\u003egetData();\n\necho $model-\u003egetLabel(); // \"TRMNL OG (1-bit)\"\necho $model-\u003egetWidth(); // 800\necho $model-\u003egetHeight(); // 480\necho $model-\u003egetColors(); // 2\necho $model-\u003egetBitDepth(); // 1\n```\n\n## Development\n\n### Running Tests\n\n```bash\ncomposer test\ncomposer test-coverage\n```\n\n### Code Quality\n\n```bash\ncomposer format\ncomposer analyse\ncomposer rector\n```\n\n## License\n\nMIT License. See LICENSE file for details.\n\n## Contributing\n\n1. Create an issue to discuss your idea\n2. Fork the repository\n3. Create a feature branch\n4. Make your changes\n5. Add tests to maintain coverage\n6. Run the test suite\n7. Submit a pull request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbnussbau%2Ftrmnl-pipeline-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbnussbau%2Ftrmnl-pipeline-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbnussbau%2Ftrmnl-pipeline-php/lists"}