Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}

5 changes: 4 additions & 1 deletion src/dymoprint/detect.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import platform
from typing import NamedTuple, NoReturn
from typing import Tuple, NamedTuple, NoReturn

import usb

Expand All @@ -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
39 changes: 30 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,9 @@ 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()

if detected_device is None:
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
2 changes: 1 addition & 1 deletion src/dymoprint/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def print_label(self):
try:
if self.label_bitmap is None:
raise RuntimeError("No label to print! Call update_label_render first.")
print_label(
print_label(None,
self.label_bitmap, self.margin.value(), self.tape_size.currentData()
)
except (RuntimeError, USBError) as err:
Expand Down