Skip to content

Commit

Permalink
LUT based label size and offset handler
Browse files Browse the repository at this point in the history
  • Loading branch information
tomek-szczesny committed Nov 10, 2023
1 parent 0a33ed7 commit c9ff02d
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 10 deletions.
6 changes: 5 additions & 1 deletion src/dymoprint/command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ def main():
justify=justify,
)

if not args.test_pattern:
label_bitmap = render_engine.add_offset(label_bitmap)

# print or show the label
if args.preview or args.preview_inverted or args.imagemagick:
print("Demo mode: showing label..")
Expand All @@ -258,4 +261,5 @@ def main():
if args.imagemagick:
ImageOps.invert(label_image).show()
else:
print_label(label_bitmap, margin_px=args.m, tape_size_mm=args.t)
print_label(render_engine.detected_device,
label_bitmap, margin_px=args.m, tape_size_mm=args.t)
28 changes: 28 additions & 0 deletions src/dymoprint/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,31 @@

DEFAULT_FONT_DIR = Path(dymoprint.resources.fonts.__file__).parent
ICON_DIR = Path(dymoprint.resources.icons.__file__).parent

# Print offset dictionaries
# The three values indicate:
# - Printer header size in dots
# - A number of the first visible dot on a test print,
# - A number of the last visible dot on a test print.
# Note the dot numeration is zero based.
# Impossible combinations (such as PnP with 19mm tape) should have sane defaults.
dict_pnp = {6: (64,11,51), 9: (64,1,62), 12: (64,0,63), 19: (64,0,63)}
dict_420p = {6: (128,44,85), 9: (128,31,94), 12: (128,38,117), 19: (128,2,127)}

# Offset meta-dictionary
# This one binds printer PID with an offset dictionary.
# All supported products must have an entry here.
OFFSETS = {
0x0011: dict_pnp,
0x0015: dict_pnp,
0x1001: dict_pnp,
0x1002: dict_pnp,
0x1003: dict_420p,
0x1004: dict_420p,
0x1005: dict_pnp,
0x1006: dict_pnp,
0x1007: dict_pnp,
0x1008: dict_pnp,
0x1009: dict_pnp
}

3 changes: 3 additions & 0 deletions src/dymoprint/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
PRINTER_INTERFACE_CLASS,
SUPPORTED_PRODUCTS,
UNCONFIRMED_MESSAGE,
OFFSETS,
)

GITHUB_LINK = "<https://github.com/computerlyrik/dymoprint/pull/56>"
Expand Down Expand Up @@ -47,6 +48,8 @@ def device_info(dev: usb.core.Device) -> str:
res += f" - {repr(intf)}\n"
return res

def get_device_offsets(det_dev, tape_size) -> tuple[int, int, int]:
return OFFSETS[det_dev.id][tape_size]

def detect_device() -> DetectedDevice:
dymo_devs = list(usb.core.find(idVendor=DEV_VENDOR, find_all=True))
Expand Down
36 changes: 27 additions & 9 deletions src/dymoprint/dymo_print_engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import usb
from PIL import Image, ImageFont, ImageOps

from dymoprint.detect import detect_device
from dymoprint.detect import detect_device, get_device_offsets

from . import DymoLabeler
from .barcode_writer import BarcodeImageWriter
Expand All @@ -18,15 +18,20 @@

class DymoRenderEngine:
tape_size_mm: int
tape_size_dots: int
offsets: tuple[int,int,int]
detected_device: DetectedDevice

def __init__(self, tape_size_mm: int = 12) -> None:
"""Initialize a DymoRenderEngine object with a specified tape size."""
self.tape_size_mm = tape_size_mm
self.detected_device = detect_device()
self.offsets = get_device_offsets(self.detected_device, self.tape_size_mm)
self.tape_size_dots = self.offsets[2] - self.offsets[1] + 1

def render_empty(self, label_len: int = 1) -> Image.Image:
"""Render an empty label image."""
label_height = DymoLabeler.max_bytes_per_line(self.tape_size_mm) * 8
return Image.new("1", (label_len, label_height))
return Image.new("1", (label_len, self.tape_size_dots))

def render_test(self, width: int = 100) -> Image.Image:
"""Render a test pattern"""
Expand Down Expand Up @@ -62,7 +67,7 @@ def render_test(self, width: int = 100) -> Image.Image:

def render_qr(self, qr_input_text: str) -> Image.Image:
"""Render a QR code image from the input text."""
label_height = DymoLabeler.max_bytes_per_line(self.tape_size_mm) * 8
label_height = self.tape_size_dots
if len(qr_input_text) == 0:
return Image.new("1", (1, label_height))

Expand Down Expand Up @@ -98,7 +103,7 @@ def render_barcode(
self, barcode_input_text: str, bar_code_type: str
) -> Image.Image:
"""Render a barcode image from the input text and barcode type."""
label_height = DymoLabeler.max_bytes_per_line(self.tape_size_mm) * 8
label_height = self.tape_size_dots
if len(barcode_input_text) == 0:
return Image.new("1", (1, label_height))

Expand All @@ -110,7 +115,7 @@ def render_barcode(
"font_size": 0,
"vertical_margin": 8,
"module_height": (
DymoLabeler.max_bytes_per_line(self.tape_size_mm) * 8 - 16
self.tape_size_dots
),
"module_width": 2,
"background": "black",
Expand Down Expand Up @@ -139,7 +144,7 @@ def render_text(
text_lines = [" "]

# create an empty label image
label_height_px = DymoLabeler.max_bytes_per_line(self.tape_size_mm) * 8
label_height_px = self.tape_size_dots
line_height = float(label_height_px) / len(text_lines)
font_size_px = int(round(line_height * font_size_ratio))

Expand Down Expand Up @@ -186,7 +191,7 @@ def render_text(
def render_picture(self, picture_path: str) -> Image.Image:
if len(picture_path):
if os.path.exists(picture_path):
label_height = DymoLabeler.max_bytes_per_line(self.tape_size_mm) * 8
label_height = self.tape_size_dots
with Image.open(picture_path) as img:
if img.height > label_height:
ratio = label_height / img.height
Expand Down Expand Up @@ -255,9 +260,23 @@ def merge_render(
return out_label_bitmap

return label_bitmap

def add_offset(self, label_bitmap) -> Image.Image:
"""Add offset specified for given printer model and tape size"""
label_padded = Image.new(
"1",
(
label_bitmap.width,
label_bitmap.height + self.offsets[0] - self.offsets[2] - 1
),
)
label_padded.paste(label_bitmap, (0, 0))
return label_padded



def print_label(
detected_device: DetectedDevice,
label_bitmap: Image.Image,
margin_px: int = DEFAULT_MARGIN_PX,
tape_size_mm: int = 12,
Expand All @@ -267,7 +286,6 @@ def print_label(
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
Expand Down

0 comments on commit c9ff02d

Please sign in to comment.