Skip to content

Commit

Permalink
More refactoring and comments
Browse files Browse the repository at this point in the history
Fix type annotation for older Python versions
  • Loading branch information
maresb committed Nov 5, 2023
1 parent 2d3fabc commit 98e8743
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 32 deletions.
6 changes: 3 additions & 3 deletions src/dymoprint/command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from PIL import Image, ImageOps

from . import __version__
from .constants import DEFAULT_MARGIN, PIXELS_PER_MM, USE_QR, e_qrcode
from .constants import DEFAULT_MARGIN_PX, PIXELS_PER_MM, USE_QR, e_qrcode
from .dymo_print_engines import DymoRenderEngine, print_label
from .font_config import font_filename
from .metadata import our_metadata
Expand Down Expand Up @@ -135,8 +135,8 @@ def parse_args():
parser.add_argument(
"-m",
type=int,
default=DEFAULT_MARGIN,
help=f"Margin in px (default is {DEFAULT_MARGIN})",
default=DEFAULT_MARGIN_PX,
help=f"Margin in px (default is {DEFAULT_MARGIN_PX})",
)
parser.add_argument(
"--scale", type=int, default=90, help="Scaling font factor, [0,10] [%%]"
Expand Down
2 changes: 1 addition & 1 deletion src/dymoprint/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

DEFAULT_FONT_STYLE = "regular"

DEFAULT_MARGIN = 56
DEFAULT_MARGIN_PX = 56

FLAG_TO_STYLE = {
"r": "regular",
Expand Down
6 changes: 5 additions & 1 deletion src/dymoprint/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@


class DetectedDevice(NamedTuple):
id: int
"""See dymoprint.constants.SUPPORTED_PRODUCTS for a list of known IDs."""
dev: usb.core.Device
intf: usb.core.Interface
devout: usb.core.Endpoint
Expand Down Expand Up @@ -124,7 +126,9 @@ def detect_device() -> DetectedDevice:

if not devout or not devin:
die("The device endpoints not be found.")
return DetectedDevice(dev, intf, devout, devin)
return DetectedDevice(
id=dev.idProduct, dev=dev, intf=intf, devout=devout, devin=devin
)


def instruct_on_access_denied(dev: usb.core.Device) -> NoReturn:
Expand Down
37 changes: 27 additions & 10 deletions src/dymoprint/dymo_print_engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from . import DymoLabeler
from .barcode_writer import BarcodeImageWriter
from .constants import DEFAULT_MARGIN, PIXELS_PER_MM, QRCode
from .constants import DEFAULT_MARGIN_PX, PIXELS_PER_MM, QRCode
from .utils import die, draw_image, scaling


Expand Down Expand Up @@ -226,32 +226,49 @@ def merge_render(


def print_label(
label_bitmap: Image.Image, margin_px: int = DEFAULT_MARGIN, tape_size_mm: int = 12
label_bitmap: Image.Image,
margin_px: int = DEFAULT_MARGIN_PX,
tape_size_mm: int = 12,
) -> None:
"""Print a label bitmap to the detected printer."""
# convert the image to the proper matrix for the dymo labeler object
"""Print a label bitmap to the detected printer.
The label bitmap is a PIL image in 1-bit format (mode=1), and pixels with value
equal to 1 are burned.
"""
detected_device = detect_device()

# Convert the image to the proper matrix for the dymo labeler object so that
# rows span the width of the label, and the first row corresponds to the left
# edge of the label.
label_rotated = label_bitmap.transpose(Image.ROTATE_270)
labelstream = label_rotated.tobytes()

# Convert the image to raw bytes. Pixels along rows are chunked into groups of
# 8 pixels, and subsequent rows are concatenated.
labelstream: bytes = label_rotated.tobytes()

# Regather the bytes into rows
label_stream_row_length = int(math.ceil(label_bitmap.height / 8))
if len(labelstream) // label_stream_row_length != label_bitmap.width:
die("An internal problem was encountered while processing the label " "bitmap!")
label_rows = [
label_rows: list[bytes] = [
labelstream[i : i + label_stream_row_length]
for i in range(0, len(labelstream), label_stream_row_length)
]
label_matrix = [array.array("B", label_row).tolist() for label_row in label_rows]

detected_device = detect_device()
# Convert bytes into ints
label_matrix: list[list[int]] = [
array.array("B", label_row).tolist() for label_row in label_rows
]

lm = DymoLabeler(
detected_device.devout,
detected_device.devin,
synwait=64,
tape_size=tape_size_mm,
tape_size_mm=tape_size_mm,
)

print("Printing label..")
lm.printLabel(label_matrix, margin=margin_px)
lm.printLabel(label_matrix, margin_px=margin_px)
print("Done printing.")
usb.util.dispose_resources(detected_device.dev)
print("Cleaned up.")
4 changes: 2 additions & 2 deletions src/dymoprint/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)
from usb.core import USBError

from .constants import DEFAULT_MARGIN, ICON_DIR
from .constants import DEFAULT_MARGIN_PX, ICON_DIR
from .dymo_print_engines import DymoRenderEngine, print_label
from .q_dymo_labels_list import QDymoLabelList

Expand Down Expand Up @@ -65,7 +65,7 @@ def init_elements(self):

self.margin.setMinimum(20)
self.margin.setMaximum(1000)
self.margin.setValue(DEFAULT_MARGIN)
self.margin.setValue(DEFAULT_MARGIN_PX)
self.tape_size.addItem("19", 19)
self.tape_size.addItem("12", 12)
self.tape_size.addItem("9", 9)
Expand Down
34 changes: 19 additions & 15 deletions src/dymoprint/labeler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
# this notice are preserved.
# === END LICENSE STATEMENT ===
import array
from typing import Optional
from typing import List, Optional

import usb

from .constants import DEFAULT_MARGIN, ESC, SYN
from .constants import DEFAULT_MARGIN_PX, ESC, SYN


class DymoLabeler:
Expand All @@ -28,6 +28,8 @@ class DymoLabeler:
<https://download.dymo.com/dymo/technical-data-sheets/LW%20450%20Series%20Technical%20Reference.pdf>
"""

tape_size_mm: int

@staticmethod
def max_bytes_per_line(tape_size_mm: int = 12) -> int:
return int(8 * tape_size_mm / 12)
Expand All @@ -42,11 +44,11 @@ def max_bytes_per_line(tape_size_mm: int = 12) -> int:
devout: usb.core.Endpoint
devin: usb.core.Endpoint

def __init__(self, devout, devin, synwait=None, tape_size=12):
def __init__(self, devout, devin, synwait=None, tape_size_mm=12):
"""Initialize the LabelManager object. (HLF)"""

self.tape_size = tape_size
self.cmd: list[int] = []
self.tape_size_mm = tape_size_mm
self.cmd: List[int] = []
self.response = False
self.bytesPerLine_ = None
self.dotTab_ = 0
Expand Down Expand Up @@ -125,7 +127,7 @@ def statusRequest(self):
def dotTab(self, value):
"""Set the bias text height, in bytes. (MLF)"""

if value < 0 or value > self.max_bytes_per_line(self.tape_size):
if value < 0 or value > self.max_bytes_per_line(self.tape_size_mm):
raise ValueError
cmd = [ESC, ord("B"), value]
self.buildCommand(cmd)
Expand All @@ -143,7 +145,9 @@ def tapeColor(self, value):
def bytesPerLine(self, value: int):
"""Set the number of bytes sent in the following lines. (MLF)"""

if value < 0 or value + self.dotTab_ > self.max_bytes_per_line(self.tape_size):
if value < 0 or value + self.dotTab_ > self.max_bytes_per_line(
self.tape_size_mm
):
raise ValueError
if value == self.bytesPerLine_:
return
Expand All @@ -168,8 +172,8 @@ def chainMark(self):
"""Set Chain Mark. (MLF)"""

self.dotTab(0)
self.bytesPerLine(self.max_bytes_per_line(self.tape_size))
self.line([0x99] * self.max_bytes_per_line(self.tape_size))
self.bytesPerLine(self.max_bytes_per_line(self.tape_size_mm))
self.line([0x99] * self.max_bytes_per_line(self.tape_size_mm))

def skipLines(self, value):
"""Set number of lines of white to print. (MLF)"""
Expand All @@ -193,16 +197,16 @@ def getStatus(self):
response = self.sendCommand()
print(response)

def printLabel(self, lines, margin=DEFAULT_MARGIN):
def printLabel(self, lines: List[List[int]], margin_px=DEFAULT_MARGIN_PX):
"""Print the label described by lines. (Automatically split label if
larger than maxLines)"""

while len(lines) > self.maxLines + 1:
self.rawPrintLabel(lines[0 : self.maxLines], margin=0)
self.rawPrintLabel(lines[0 : self.maxLines], margin_px=0)
del lines[0 : self.maxLines]
self.rawPrintLabel(lines, margin=margin)
self.rawPrintLabel(lines, margin_px=margin_px)

def rawPrintLabel(self, lines, margin=DEFAULT_MARGIN):
def rawPrintLabel(self, lines: List[List[int]], margin_px=DEFAULT_MARGIN_PX):
"""Print the label described by lines. (HLF)"""

# optimize the matrix for the dymo label printer
Expand All @@ -219,8 +223,8 @@ def rawPrintLabel(self, lines, margin=DEFAULT_MARGIN):
self.dotTab(dottab)
for line in lines:
self.line(line)
if margin > 0:
self.skipLines(margin * 2)
if margin_px > 0:
self.skipLines(margin_px * 2)
self.statusRequest()
response = self.sendCommand()
print(f"Post-send response: {response}")

0 comments on commit 98e8743

Please sign in to comment.