Skip to content

Commit

Permalink
add API endpoint to print image
Browse files Browse the repository at this point in the history
  • Loading branch information
FriedrichFroebel committed May 26, 2024
1 parent 8d3fac1 commit 5f96214
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 5 deletions.
5 changes: 5 additions & 0 deletions brother_ql_web/labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class LabelParameters:
font_family: str | None = None
font_style: str | None = None
text: str = ""
image: bytes | None = None
pdf: bytes | None = None
font_size: int = 100
label_size: str = "62"
margin: int = 10
Expand Down Expand Up @@ -169,6 +171,9 @@ def _determine_text_offsets(


def create_label_image(parameters: LabelParameters) -> Image.Image:
if parameters.image:
return Image.open(BytesIO(parameters.image))

image_font = ImageFont.truetype(parameters.font_path, parameters.font_size)

# Workaround for a bug in multiline_textsize()
Expand Down
64 changes: 60 additions & 4 deletions brother_ql_web/web.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from __future__ import annotations

import logging
from io import BytesIO
from pathlib import Path
from typing import Any, cast, Dict # TODO: Remove `Dict` after dropping Python 3.8.

import bottle
from brother_ql import BrotherQLRaster

from brother_ql_web.configuration import Configuration
from brother_ql_web.labels import (
LabelParameters,
Expand Down Expand Up @@ -52,7 +55,18 @@ def labeldesigner() -> dict[str, Any]:
}


def get_label_parameters(request: bottle.BaseRequest) -> LabelParameters:
def _save_to_bytes(upload: bottle.FileUpload | None) -> bytes | None:
if upload is None:
return None
output = BytesIO()
upload.save(output)
output.seek(0)
return output.getvalue()


def get_label_parameters(
request: bottle.BaseRequest, should_be_file: bool = False
) -> LabelParameters:
# As we have strings, *bottle* would try to generate Latin-1 bytes from it
# before decoding it back to UTF-8. This seems to break some umlauts, thus
# resulting in UnicodeEncodeErrors being raised when going back to UTF-8.
Expand All @@ -66,10 +80,19 @@ def get_label_parameters(request: bottle.BaseRequest) -> LabelParameters:
parameters.recode_unicode = False
d = parameters.decode() # UTF-8 decoded form data

font_family = d.get("font_family").rpartition("(")[0].strip()
font_style = d.get("font_family").rpartition("(")[2].rstrip(")")
try:
font_family = d.get("font_family").rpartition("(")[0].strip()
font_style = d.get("font_family").rpartition("(")[2].rstrip(")")
except AttributeError:
if should_be_file:
font_family = ""
font_style = ""
else:
raise
context = {
"text": d.get("text", ""),
"image": _save_to_bytes(request.files.get("image")),
"pdf": _save_to_bytes(request.files.get("pdf")),
"font_size": int(d.get("font_size", 100)),
"font_family": font_family,
"font_style": font_style,
Expand Down Expand Up @@ -110,7 +133,7 @@ def get_preview_image() -> bytes:
@bottle.get("/api/print/text") # type: ignore[misc]
def print_text() -> dict[str, bool | str]:
"""
API to print a label
API to print some text
returns: JSON
"""
Expand All @@ -132,6 +155,39 @@ def print_text() -> dict[str, bool | str]:
save_image_to="sample-out.png" if bottle.DEBUG else None,
)

return _print(parameters=parameters, qlr=qlr)


@bottle.post("/api/print/image") # type: ignore[misc]
def print_image() -> dict[str, bool | str]:
"""
API to print an image
returns: JSON
"""
return_dict: dict[str, bool | str] = {"success": False}

try:
parameters = get_label_parameters(bottle.request, should_be_file=True)
except (AttributeError, LookupError) as e:
return_dict["error"] = str(e)
return return_dict

if parameters.image is None or not parameters.image:
return_dict["error"] = "Please provide the label image"
return return_dict

qlr = generate_label(
parameters=parameters,
configuration=cast(Configuration, get_config("brother_ql_web.configuration")),
)

return _print(parameters=parameters, qlr=qlr)


def _print(parameters: LabelParameters, qlr: BrotherQLRaster) -> dict[str, bool | str]:
return_dict: dict[str, bool | str] = {"success": False}

if not bottle.DEBUG:
try:
print_label(
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ requires = [
[tool.mypy]
mypy_path = '$MYPY_CONFIG_FILE_DIR/stubs'
strict = true
files = "brother_ql_web,tests"
3 changes: 2 additions & 1 deletion stubs/bottle.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ class BaseRequest:
def forms(self): ...
@property
def params(self) -> FormsDict: ...
def files(self): ...
@property
def files(self) -> FormsDict: ...
def json(self): ...
@property
def body(self): ...
Expand Down
48 changes: 48 additions & 0 deletions tests/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,5 +283,53 @@ def test_regular_mode(self) -> None:
self.assertEqual(expected, fd.read())


class PrintImageTestCase(TestCase):
def test_error__empty_image(self) -> None:
self.run_server()
response = requests.post(
url="http://localhost:8013/api/print/image", files={"image": b""}
)
self.assertEqual(200, response.status_code)
self.assertEqual(
b'{"success": false, "error": "Please provide the label image"}',
response.content,
)
with open(cast(str, self.printer_file), mode="rb") as fd:
self.assertEqual(b"", fd.read())

def test_error__no_image(self) -> None:
self.run_server()
response = requests.post(url="http://localhost:8013/api/print/image")
self.assertEqual(200, response.status_code)
self.assertEqual(
b'{"success": false, "error": "Please provide the label image"}',
response.content,
)
with open(cast(str, self.printer_file), mode="rb") as fd:
self.assertEqual(b"", fd.read())

def test_print_image(self) -> None:
image = files("tests") / "data" / "hello_world.png"
reference = (
files("tests") / "data" / "hello_world__label_size_62__standard.data"
)
with as_file(image) as path:
image_data = path.read_bytes()
expected = reference.read_bytes()

self.run_server()
response = requests.post(
url=(
"http://localhost:8013/api/print/image?"
"label_size=62&orientation=standard"
),
files={"image": image_data},
)
self.assertEqual(200, response.status_code)
self.assertEqual(b'{"success": true}', response.content)
with open(cast(str, self.printer_file), mode="rb") as fd:
self.assertEqual(expected, fd.read())


class MainTestCase(TestCase):
pass

0 comments on commit 5f96214

Please sign in to comment.