Restructure into fichero/ package and fix review issues
Split monolithic printer.py into fichero/{printer,imaging,cli}.py.
Add asyncio.Lock to prevent notification buffer races, replace sys.exit
with PrinterNotFound/PrinterTimeout/PrinterNotReady exceptions, name all
magic sleep values as DELAY_* constants, add image_to_raster validation,
add --font-size and --label-height to text subcommand, add input
validation to set command, add type annotations to public API, fix
misleading paper type comment, remove duplicate PROTOCOL.md section,
update README for new CLI entry point and library usage.
This commit is contained in:
50
fichero/imaging.py
Normal file
50
fichero/imaging.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""Image processing for Fichero D11s thermal label printer."""
|
||||
|
||||
import logging
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from fichero.printer import PRINTHEAD_PX
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def prepare_image(img: Image.Image, max_rows: int = 240) -> Image.Image:
|
||||
"""Convert any image to 96px wide, 1-bit, black on white."""
|
||||
img = img.convert("L")
|
||||
w, h = img.size
|
||||
new_h = int(h * (PRINTHEAD_PX / w))
|
||||
if new_h > max_rows:
|
||||
log.warning("Image height %dpx exceeds max %dpx, cropping bottom", new_h, max_rows)
|
||||
new_h = max_rows
|
||||
img = img.resize((PRINTHEAD_PX, new_h), Image.LANCZOS)
|
||||
img = img.point(lambda x: 1 if x < 128 else 0, "1")
|
||||
return img
|
||||
|
||||
|
||||
def image_to_raster(img: Image.Image) -> bytes:
|
||||
"""Pack 1-bit image into raw raster bytes, MSB first."""
|
||||
if img.mode != "1":
|
||||
raise ValueError(f"Expected mode '1', got '{img.mode}'")
|
||||
if img.width != PRINTHEAD_PX:
|
||||
raise ValueError(f"Expected width {PRINTHEAD_PX}, got {img.width}")
|
||||
return img.tobytes()
|
||||
|
||||
|
||||
def text_to_image(text: str, font_size: int = 24, label_height: int = 240) -> Image.Image:
|
||||
"""Render text in landscape, then rotate 90 degrees for label printing."""
|
||||
canvas_w = label_height
|
||||
canvas_h = PRINTHEAD_PX
|
||||
img = Image.new("L", (canvas_w, canvas_h), 255)
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
font = ImageFont.load_default(size=font_size)
|
||||
|
||||
bbox = draw.textbbox((0, 0), text, font=font)
|
||||
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
||||
x = (canvas_w - tw) // 2
|
||||
y = (canvas_h - th) // 2
|
||||
draw.text((x, y), text, fill=0, font=font)
|
||||
|
||||
img = img.rotate(90, expand=True)
|
||||
return img
|
||||
Reference in New Issue
Block a user