# app.py from io import BytesIO from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field from typing import List from PIL import Image, ImageOps import treepoem from brother_ql.raster import BrotherQLRaster from brother_ql.conversion import convert from brother_ql.backends.helpers import send if not hasattr(Image, "ANTIALIAS"): Image.ANTIALIAS = Image.Resampling.LANCZOS app = FastAPI(title="QL-1060N Stacked Barcode Printer") class PrintRequest(BaseModel): data: str = Field(..., description="String to encode") printer_ip: str = Field( "192.168.178.49", description="Brother QL-1060N IP on your LAN" ) model: str = Field("QL-1060N", description="Brother model") label: str = Field("12", description="12 mm continuous tape") def make_barcode_image(data: str, symb: str) -> Image.Image: """Generate a monochrome barcode image via BWIPP.""" if symb == "code128": img = treepoem.generate_barcode( barcode_type="code128", data=data, options={"includetext": True, "height": "0.3"}, scale=3, ) else: # rectangular DataMatrix img = treepoem.generate_barcode( barcode_type="datamatrixrectangular", data=data, options={}, # options={"columns": "16", "rows": "48"}, scale=3, ) return img.convert("1") def compose_label(images: List[Image.Image]) -> Image.Image: """ Horizontally stack each barcode image with padding, on a single monochrome canvas. """ border = 10 # white border around each barcode spacing = 20 # gap between them # add border framed = [ImageOps.expand(img, border=border, fill="white") for img in images] max_h = max(img.height for img in framed) total_w = sum(img.width for img in framed) + spacing * (len(framed) - 1) canvas = Image.new("1", (total_w, max_h), 1) x = 0 for img in framed: y = (max_h - img.height) // 2 canvas.paste(img, (x, y)) x += img.width + spacing return canvas def send_to_printer(image: Image.Image, printer_ip: str, model: str, label: str): """ Rasterize the PIL image for Brother QL and send it via network. """ qlr = BrotherQLRaster(model) qlr.exception_on_warning = True instructions = convert( qlr=qlr, images=[image], label=label, rotate="90", threshold=70.0, dither=False, compress=True, cut=True, ) send( instructions=instructions, printer_identifier=printer_ip, backend_identifier="network", blocking=True, ) @app.post("/print", summary="Print Code128 + DataMatrix side-by-side") def print_stacked(req: PrintRequest): try: code_img = make_barcode_image(req.data, "code128") dm_img = make_barcode_image(req.data, "datamatrix_rect") label_img = compose_label([code_img, dm_img]) send_to_printer(label_img, req.printer_ip, req.model, req.label) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) return { "status": "printed", "model": req.model, "label_width_mm": req.label, "layout": "code128 + datamatrix side by side", }