Skip to main content

Rootless Brother QL-800 label printing without proprietary software

Project description

ohbrother

Brother QL-800 label printing on Linux without proprietary software.

Requires Python 3.11+. Dependencies: pyusb for USB I/O, Pillow for image handling. Rasterization is implemented in-house.

Background

The QL-800 presents as USB class 8 (Mass Storage / BOT) in all modes, including printer mode. The standard brother_ql pyusb backend only scans for class-7 devices and never finds it. This library talks directly to the bulk endpoints and handles two QL-800-specific quirks:

  • Mid-stream status response. The raster stream embeds ESC i S; the printer sends a 32-byte response mid-write. Not draining it before the next status read returns stale data.
  • Zero-length packets. The IN endpoint sends ZLPs when idle instead of timing out.

Installation

pip install ohbrother
sudo ohbrother-install-udev

Reconnect the printer after installing the udev rules.

Editor Lite mode

If the printer enumerates as PID 0x209e, it is in Editor Lite (virtual disk) mode. Hold the Editor Lite button until the green LED turns off, then reconnect. It re-enumerates as 0x209b. ohbrother raises EditorLiteModeError on 0x209e.

Usage

ohbrother detect
ohbrother status
ohbrother list-labels                              # shows pixel dimensions too

# Endless tape
ohbrother print "Hello World"
ohbrother print --label 29 "narrow 29mm label"

# Die-cut labels — image is sized to exact label dimensions
ohbrother print --label 29x90 "Jane Smith\n123 Main St\nSpringfield IL"
ohbrother print --label 62x100 --image shipping.png

# Two-color tape (DK-22251)
ohbrother print --label 62red --red "black text"
ohbrother print --label 62red --red --text-color red "red text"

# Utilities
ohbrother cut                                      # feed and cut without printing
ohbrother print --dry-run "test"

Python API

from ohbrother import Printer, PrintOptions, render_for_label, label_dims

# Endless tape, black text
opts = PrintOptions(label="62")
with Printer("usb://0x04f9:0x209b", opts) as p:
    img = render_for_label("Hello World", "62")
    p.print_images([img])

# Die-cut label (29x90mm standard address label)
# render_for_label sizes the canvas to exactly (306, 991)px as required
opts = PrintOptions(label="29x90")
with Printer("usb://0x04f9:0x209b", opts) as p:
    img = render_for_label("Jane Smith\n123 Main St", "29x90", font_size=50)
    p.print_images([img])

# Two-color tape — red text
opts = PrintOptions(label="62red", red=True)
with Printer("usb://0x04f9:0x209b", opts) as p:
    img = render_for_label("URGENT", "62red", text_color=(255, 0, 0), font_size=140)
    p.print_images([img])

# Feed and cut without printing
with Printer("usb://0x04f9:0x209b", opts) as p:
    p.cut()

# Recover from a stuck state
with Printer("usb://0x04f9:0x209b") as p:
    p.reset()

Auto-detect the first available printer:

from ohbrother.detect import discover

identifier = next(d["identifier"] for d in discover() if d["usable"])

Die-cut labels

Die-cut labels require the image to be exactly (width_px, height_px) — any mismatch raises a ValueError. render_for_label() handles this automatically. If you construct images manually, use label_dims(label_id) to get the required pixel dimensions.

Two-color tape (DK-22251)

Always use label="62red" and red=True, even for black-only prints. Single-color mode against two-color tape produces a generic error with no error bits; validate_preflight() catches this before the write and raises TapeMismatchError.

Red vs black separation uses HSV: hue < 40 or > 210, saturation > 100, value > 80. (255, 0, 0) renders as red ink; (0, 0, 0) renders as black. Both can appear in the same image.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ohbrother-0.1.1.tar.gz (21.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ohbrother-0.1.1-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

File details

Details for the file ohbrother-0.1.1.tar.gz.

File metadata

  • Download URL: ohbrother-0.1.1.tar.gz
  • Upload date:
  • Size: 21.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for ohbrother-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9f9b951a97b113853d4caae088340201f392c56f7879fda101441744fdf57722
MD5 25c601893802483b22e27e6306439c11
BLAKE2b-256 61c82fc0982d1e8f5c22105c43e0263816cbf92b9283e1eded3c7ecd3920169b

See more details on using hashes here.

File details

Details for the file ohbrother-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: ohbrother-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 22.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for ohbrother-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 91fd982b70919b16436174b661ade2f3d035e8ccb06e642d7d908ea270910bec
MD5 22adfdb82b53f3b7818f9d8b7ad74fcc
BLAKE2b-256 0cde4663ed45a575eff097efa372493333b68bbb3ad609bdc40b8a5d13ac9ba1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page