switch from treepoem
This commit is contained in:
parent
c6dcb50bbb
commit
85b466f482
3 changed files with 77 additions and 56 deletions
83
app.py
83
app.py
|
|
@ -1,14 +1,18 @@
|
|||
# app.py
|
||||
import asyncio
|
||||
from functools import lru_cache
|
||||
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 barcode import Code128
|
||||
from barcode.writer import ImageWriter
|
||||
from brother_ql.backends.helpers import send
|
||||
from brother_ql.conversion import convert
|
||||
from brother_ql.raster import BrotherQLRaster
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from PIL import Image, ImageOps
|
||||
from pydantic import BaseModel, Field
|
||||
from pylibdmtx.pylibdmtx import encode
|
||||
|
||||
if not hasattr(Image, "ANTIALIAS"):
|
||||
Image.ANTIALIAS = Image.Resampling.LANCZOS
|
||||
|
|
@ -16,6 +20,29 @@ if not hasattr(Image, "ANTIALIAS"):
|
|||
app = FastAPI(title="QL-1060N Stacked Barcode Printer")
|
||||
|
||||
|
||||
@lru_cache(maxsize=256)
|
||||
def make_code128(data: str) -> Image.Image:
|
||||
writer_opts = {
|
||||
"module_height": 15.0,
|
||||
"module_width": 0.5,
|
||||
"quiet_zone": 1.0,
|
||||
"font_size": 10,
|
||||
"text_distance": 1.0,
|
||||
}
|
||||
code = Code128(
|
||||
data, writer=ImageWriter(), add_checksum=False, writer_options=writer_opts
|
||||
)
|
||||
pil_img = code.render(writer_opts) # returns RGB
|
||||
return pil_img.convert("1")
|
||||
|
||||
|
||||
@lru_cache(maxsize=256)
|
||||
def make_datamatrix(data: str) -> Image.Image:
|
||||
dm = encode(data.encode("utf-8"))
|
||||
pil_img = Image.frombytes("RGB", (dm.width, dm.height), dm.pixels)
|
||||
return pil_img.convert("1")
|
||||
|
||||
|
||||
class PrintRequest(BaseModel):
|
||||
data: str = Field(..., description="String to encode")
|
||||
printer_ip: str = Field(
|
||||
|
|
@ -25,35 +52,8 @@ class PrintRequest(BaseModel):
|
|||
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=2,
|
||||
)
|
||||
else: # rectangular DataMatrix
|
||||
img = treepoem.generate_barcode(
|
||||
barcode_type="datamatrixrectangular",
|
||||
data=data,
|
||||
options={},
|
||||
# options={"columns": "16", "rows": "48"},
|
||||
scale=2,
|
||||
)
|
||||
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
|
||||
border, spacing = 10, 20
|
||||
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)
|
||||
|
|
@ -69,9 +69,6 @@ def compose_label(images: List[Image.Image]) -> Image.Image:
|
|||
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -79,7 +76,7 @@ def send_to_printer(image: Image.Image, printer_ip: str, model: str, label: str)
|
|||
qlr=qlr,
|
||||
images=[image],
|
||||
label=label,
|
||||
rotate="90",
|
||||
rotate="90", # landscape
|
||||
threshold=70.0,
|
||||
dither=False,
|
||||
compress=True,
|
||||
|
|
@ -95,12 +92,16 @@ def send_to_printer(image: Image.Image, printer_ip: str, model: str, label: str)
|
|||
|
||||
|
||||
@app.post("/print", summary="Print Code128 + DataMatrix side-by-side")
|
||||
def print_stacked(req: PrintRequest):
|
||||
async def print_stacked(req: PrintRequest):
|
||||
try:
|
||||
code_img = make_barcode_image(req.data, "code128")
|
||||
dm_img = make_barcode_image(req.data, "datamatrix_rect")
|
||||
loop = asyncio.get_running_loop()
|
||||
code_task = loop.run_in_executor(None, make_code128, req.data)
|
||||
dm_task = loop.run_in_executor(None, make_datamatrix, req.data)
|
||||
code_img, dm_img = await asyncio.gather(code_task, dm_task)
|
||||
|
||||
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))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue