feat: add docker support and configurable printer width#77
Conversation
Adds a Dockerfile, docker-compose.yaml, and .dockerignore for running Estrella as a container. The image is built locally; publishing is left as a future improvement. The serve command gains a --width flag (also readable from the PRINTER_WIDTH env var) specifying the printer's dot width. Defaults to 576 to match the TSP650II. Set to 384 for 58mm printers like the mC-Print2. PRINTER_DEVICE and LISTEN_ADDR env vars are added for the other serve options for consistency. All server handlers (photo, patterns, weave) now use the configured width instead of a hardcoded reference to PrinterConfig::TSP650II. Dividers in the patterns and weave handlers derive their character width from the printer dot width so they don't wrap on narrower paper. Adds PrinterConfig::MCP21 (384 dots, 58mm) alongside the existing TSP650II constant, and a PrinterConfig::with_width() constructor for building a config from an arbitrary dot width. README gets a Docker section covering the build, bluetooth setup, docker compose usage, environment variables, and persisting the RFCOMM binding with a systemd unit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same fix as patterns and weave: derive chars_per_line from printer_width instead of using Divider::default(), which assumed TSP650II column count. Also threads printer_width into the preview handler. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The env attribute on #[arg] requires clap's env feature flag. The PrinterConfig import in receipt.rs was unused after refactoring build_receipt to take a raw u16 width. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Converted to draft because I've found some other improvements I'm experimenting with :) |
The Markdown component gains two new optional fields: - `size: [u8; 2]`: height and width multipliers for body text (0 = 1×, 1 = 2×, etc.), defaulting to [0, 0] (no scaling). Heading sizes within the document are unaffected; this controls the base font size for paragraph text only. - `chars_per_line: Option<usize>`: when set, body text is word-wrapped to this column width at 1× size. The emit logic divides by the width multiplier to find the effective wrap column, so the same field works correctly at any scale. Word-wrapping is performed in a new `emit_wrapped` helper that tracks the current column position and inserts newlines at word boundaries. Soft breaks are suppressed when word-wrapping is active (they are handled implicitly as spaces). Hard breaks and paragraph ends reset the column counter. Both fields default to their zero/None values, so all existing documents and API callers are unaffected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pass `chars_per_line` (derived from `printer_width`) into the Markdown component so body text word-wraps correctly on narrow paper. Also reduce the default receipt title from size [3, 2] to [1, 1] — the original value was calibrated for 80mm paper and produces oversized output on 58mm printers. Add `TZ` as a commented-out example in docker-compose.yaml. Timestamps in printed receipts use the system clock, so without a TZ env var the container defaults to UTC; setting this to the local timezone gives correct printed times without rebuilding the image. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `title_size` and `body_size` fields to `ReceiptForm` so callers can override the character size multipliers per-request without changing any defaults. Both accept a `[height, width]` array where 0 = 1×, 1 = 2×, etc. Defaults are unchanged from the original: `title_size` defaults to [3, 2] and `body_size` defaults to [0, 0] (normal size), so existing API callers are unaffected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
hey sorry just coming back from holidays and noticed this, will take a closer look tomorrow! thanks for posting! |
|
hey, these are all great changes but it's overwhelming if they're all mixed in the same PR. We need to discuss about each change in isolation. |
No problem, I had the same thought. Will split them up when I have time, potentially later today. |
|
Amazing! I'll be happy to review, thanks again for the contributions! |
Full disclosure: I used Claude Code (Sonnet 4.6) to help me with this as I haven't used Rust before and wanted to get this working quickly. It's not a huge change though and knowing other languages gives me enough confidence to say that this should be mostly harmless.
I bought a Star Micronics mC-Print2 MCP21LB receipt printer today and wanted to get it working with this tool, but I don't use Nix - all my self-hosted services are deployed via Docker Compose. So I've added that as an installation option, albeit without a published image. This will just build the image locally.
Then I discovered my printer prints narrower paper than the TSP650II, so I had to add configurable width so that images and separators didn't get wrapped or horizontally cut off.
Works great now:
(I also 3D-printed a receipt spike/holder using this design).