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
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,50 @@ The most important command is the `print` command and here is its CLI signature:
--margin Print margin
--help Show this message and exit.

## Printing text labels

Some people may wish to automatically print text on their labels, rather
than having to manually generate images first. For this, `brother_pt` has
the `text` command, which takes one or more lines of text to be printed as
parameters (be sure to quote them in your shell!). Consider something along
the lines of:

```
$ brother_pt text --preview "This is a larger label, rendered to your screen only."
$ brother_pt text "This is a larger label."
$ brother_pt text -f 1 "This is a single line in a smaller font."
$ brother_pt text "Here is one line" "and here is a second line."
$ brother_pt text "A label printer" "prints three lines at a time, but" "only haiku."
```

You can also specify a font with `--font` and `--size` (both must be
specified; `--font` takes a fontconfig-style pattern; consider something
along the lines of `--font "Comic Sans MS:style=Bold" --size 40` to show
that you are serious about your labels.)

### Default fonts

The default fonts selected are those distributed with the P-Touch software
-- in particular, the Helsinki font family, which seems to be hinted well
for black and white rendering on a P-Touch printer. To install these
fonts, either [install the latest P-Touch Editor software on your
Mac](https://www.brother-usa.com/ptouch/ptouch-label-editor-software), or if
on Linux, download the Windows version of the software and extract the fonts
with something like:

```
$ cabextract pew67001.exe ptedit6.msi # grab the installer bundle out of the InstallShield archive
$ msiextract ptedit6.msi # unpack the installer bundle TO THE CURRENT DIRECTORY
$ mv .\:Fonts/ Fonts
$ mv ./Program\ Files/Brother/P-touch\ Editor/6/Fonts/* Fonts/
$ cp Fonts/BRHE* ~/.local/share/fonts/ # install at least the Helsinki font set
$ fc-cache -f -v # update fontconfig
```

## Author

* Thomas Reidemeister
* Text rendering support was contributed by Joshua Wise

## Contributing

Expand Down
93 changes: 91 additions & 2 deletions brother_pt/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
"""
import argparse

import PIL.ImageOps

from brother_pt import VERSION
from .printer import *

from .cmd import *
from .font import *

def show_status(serial):
printers = find_printers(serial)
Expand All @@ -27,6 +30,7 @@ def show_status(serial):
found_printer = BrotherPt(printers[0].serial_number)
print("%s %s (%s):" % (printers[0].manufacturer, printers[0].product, printers[0].serial_number))
print(" + Media width: %dmm" % found_printer.media_width)
print(" (%dpx)" % MediaWidthToTapeMargin.to_print_width(found_printer.media_width))
print(" + Media type : %s" % found_printer.media_type.name)
print(" + Tape color : %s" % found_printer.tape_color.name)
print(" + Text color : %s" % found_printer.text_color.name)
Expand Down Expand Up @@ -85,6 +89,80 @@ def do_print(args):
return 0


def do_text(args):
printers = find_printers(args.printer)
if len(printers) == 0:
print("No supported printers found, make sure the device is switched on", file=sys.stderr)
return 1

found_printer = BrotherPt(printers[0].serial_number)
required_height = MediaWidthToTapeMargin.to_print_width(found_printer.media_width)

FONT_DEFAULTS = [
( "Helsinki Narrow:style=Bold", 40, 6 ),
( "Helsinki", 22, 8 ),
( "Helsinki", 22, 4 ),
]
baseline_spacing = 4

fontpattern = args.font
fontsize = args.size
if args.font and args.font.isdigit():
# The font is a number -- so we choose it as an index into
# FONT_DEFAULTS.
fontpattern, fontsize, baseline_spacing = FONT_DEFAULTS[int(args.font)]
font = Font(pattern = fontpattern, size = fontsize)
totheight = 0
for t in args.text:
if totheight:
totheight += baseline_spacing
w,h,baseline = font.text_dimensions(t)
totheight += h
if totheight > required_height:
print(f"text does not fit on label with font {args.font} -- total height is {totheight} px, but label is {required_height} px!")
return 1
elif not fontpattern or not fontsize:
# Try to pick some sensible defaults.
nlines = 1
print(f"{nlines} lines on media size {required_height} px")
for fontpattern, fontsize, baseline_spacing in FONT_DEFAULTS:
font = Font(pattern = fontpattern, size = fontsize)
totheight = 0
for t in args.text:
if totheight:
totheight += baseline_spacing
w,h,baseline = font.text_dimensions(t)
totheight += h
print(f"{fontpattern} @ {fontsize} is {totheight} px")
if totheight <= required_height:
print("that fits, good enough for me")
break
else:
font = Font(pattern = fontpattern, size = fontsize)
image = font.render_texts(args.text, height = required_height, vcenter = True, hcenter = False, spacing = baseline_spacing).pixels

# Margin check
margin = args.margin
if (image.width + margin) < MINIMUM_TAPE_POINTS:
print("Image (%i) + cut margin (%i) is smaller than minimum tape width (%i) ...\n"
"cutting length will be extended" % (image.width, margin, MINIMUM_TAPE_POINTS))
margin = MINIMUM_TAPE_POINTS - image.width

if args.preview:
w,h = image.size
im2 = Image.new('L', (w + margin * 2, h), 0)
im2.paste(image, (margin, 0))
PIL.ImageOps.invert(im2).show()
return

# Raster image
data = raster_image(image, found_printer.media_width)

found_printer.print_data(data, margin)

return 0


def list_printers(serial):
printers = find_printers(serial)
if len(printers) == 0:
Expand Down Expand Up @@ -120,7 +198,7 @@ def cli():
discover.set_defaults(cmd='info')

# Complex subparsers
print_menu = subparsers.add_parser('print')
print_menu = subparsers.add_parser('print', help="Print an image")
print_menu.add_argument("-r", "--rotate", default='auto',
choices=['auto', '0', '90', '180', '270'],
help='Rotate the image (counter clock-wise) by this amount of degrees. '
Expand All @@ -133,6 +211,15 @@ def cli():
print_menu.add_argument("-f", "--file", type=str, required=True, help="Image file to print")
print_menu.set_defaults(cmd='print')

text_menu = subparsers.add_parser('text', help="Print some text")
text_menu.add_argument("-m", "--margin", type=int, default=50,
help="Print margin in dots.")
text_menu.add_argument("-f", "--font", type=str, default=None, help="Font face, or a number specifying an index into the default table of fonts")
text_menu.add_argument("-s", "--size", type=int, default=None, help="Font size")
text_menu.add_argument("--preview", action='store_true', help="just show a preview, don't print a label")
text_menu.add_argument("text", nargs='+', help="Text to print")
text_menu.set_defaults(cmd='text')

args = parser.parse_args()

if args.version:
Expand All @@ -155,6 +242,8 @@ def cli():
return 0
elif args.cmd == 'print':
return do_print(args)
elif args.cmd == 'text':
return do_text(args)

return 0

Expand Down
3 changes: 2 additions & 1 deletion brother_pt/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
PRINT_HEAD_PINS = 128
USBID_BROTHER = 0x04f9
LINE_LENGTH_BYTES = 0x10
MINIMUM_TAPE_POINTS = 174 # 25.4 mm @ 180dpi
#MINIMUM_TAPE_POINTS = 174 # 25.4 mm @ 180dpi
MINIMUM_TAPE_POINTS = 50
USB_OUT_EP_ID = 0x2
USB_IN_EP_ID = 0x81
USB_TRX_TIMEOUT_MS = 15000
Expand Down
Loading