QL-800 series: printing black/red/white labels (CLI: --red)

This commit is contained in:
Philipp Klaus
2017-09-18 23:09:39 +02:00
parent f1fd99f9a7
commit ed97975b9c
4 changed files with 114 additions and 25 deletions
+4 -2
View File
@@ -15,7 +15,7 @@ The following printers are claimed to be supported (✓ means verified by the au
* QL-500 (✓), QL-550 (✓), QL-560, QL-570 (✓), QL-580N, QL-650TD, QL-700 (✓), QL-710W (✓), QL-720NW (✓), QL-800, QL-810W, QL-820NWB (✓), QL-1050, and QL-1060N.
The new QL-800 series can print labels with two colors (black and red) on DK-22251 labels. This is not yet supported by this package.
The new QL-800 series can print labels with two colors (black and red) on DK-22251 labels.
## Why
@@ -63,7 +63,7 @@ giving:
usage: brother_ql_create [-h] [--model MODEL] [--label-size LABEL_SIZE]
[--rotate {0,90,180,270}] [--threshold THRESHOLD]
[--dither] [--compress] [--no-cut]
[--dither] [--compress] [--red] [--no-cut]
[--loglevel LOGLEVEL]
image [outfile]
@@ -90,6 +90,8 @@ giving:
set, --threshold is meaningless.
--compress, -c Enable compression (if available with the model).
Takes more time but results in smaller file size.
--red Create a label to be printed in black/red/white (only
with QL-800, QL-810W, QL-820NWB on DK-22251 labels).
--no-cut Don't cut the tape after printing the label.
--loglevel LOGLEVEL Set to DEBUG for verbose debugging output to stderr.
+35 -11
View File
@@ -5,11 +5,12 @@ from __future__ import division
import sys, argparse, logging
from PIL import Image
import PIL.ImageOps
import PIL.ImageOps, PIL.ImageChops
from brother_ql.raster import BrotherQLRaster
from brother_ql.devicedependent import models, label_type_specs, ENDLESS_LABEL, DIE_CUT_LABEL, ROUND_DIE_CUT_LABEL
from brother_ql import BrotherQLError, BrotherQLUnsupportedCmd, BrotherQLUnknownModel
from brother_ql.image_trafos import filtered_hls
try:
stdout = sys.stdout.buffer
@@ -33,6 +34,7 @@ def main():
parser.add_argument('--threshold', '-t', type=float, default=70.0, help='The threshold value (in percent) to discriminate between black and white pixels.')
parser.add_argument('--dither', '-d', action='store_true', help='Enable dithering when converting the image to b/w. If set, --threshold is meaningless.')
parser.add_argument('--compress', '-c', action='store_true', help='Enable compression (if available with the model). Takes more time but results in smaller file size.')
parser.add_argument('--red', action='store_true', help='Create a label to be printed in black/red/white (only with QL-800, QL-810W, QL-820NWB on DK-22251 labels).')
parser.add_argument('--no-cut', dest='cut', action='store_false', help="Don't cut the tape after printing the label.")
parser.add_argument('--loglevel', type=lambda x: getattr(logging, x), default=logging.WARNING, help='Set to DEBUG for verbose debugging output to stderr.')
args = parser.parse_args()
@@ -54,11 +56,11 @@ def main():
qlr.exception_on_warning = True
create_label(qlr, args.image, args.label_size, threshold=args.threshold, cut=args.cut, rotate=args.rotate, dither=args.dither, compress=args.compress)
create_label(qlr, args.image, args.label_size, threshold=args.threshold, cut=args.cut, rotate=args.rotate, dither=args.dither, compress=args.compress, red=args.red)
args.outfile.write(qlr.data)
def create_label(qlr, image, label_size, threshold=70, cut=True, dither=False, compress=False, **kwargs):
def create_label(qlr, image, label_size, threshold=70, cut=True, dither=False, compress=False, red=False, **kwargs):
label_specs = label_type_specs[label_size]
dots_printable = label_specs['dots_printable']
@@ -101,15 +103,33 @@ def create_label(qlr, image, label_size, threshold=70, cut=True, dither=False, c
new_im.paste(im, (device_pixel_width-im.size[0]-right_margin_dots, 0))
im = new_im
im = im.convert("L")
im = PIL.ImageOps.invert(im)
if red:
filter_h = lambda h: 255 if h < 60 or h > 240 else 0
filter_l = lambda l: 255 if l < 220 else 0
filter_s = lambda s: 255 if s > 100 else 0
red_im = filtered_hls(im, filter_h, filter_l, filter_s)
red_im = red_im.convert("L")
red_im = PIL.ImageOps.invert(red_im)
red_im = red_im.convert("1", dither=Image.NONE)
if dither:
im = im.convert("1", dither=Image.FLOYDSTEINBERG)
filter_h = lambda h: 255
filter_l = lambda l: 255 if l < 120 else 0
filter_s = lambda s: 255
black_im = filtered_hls(im, filter_h, filter_l, filter_s)
black_im = black_im.convert("L")
black_im = PIL.ImageOps.invert(black_im)
black_im = black_im.convert("1", dither=Image.NONE)
black_im = PIL.ImageChops.subtract(black_im, red_im)
else:
threshold = 100.0 - threshold
threshold = min(255, max(0, int(threshold/100.0 * 255))) # from percent to pixel val
im = im.point(lambda x: 0 if x < threshold else 255, mode="1")
im = im.convert("L")
im = PIL.ImageOps.invert(im)
if dither:
im = im.convert("1", dither=Image.FLOYDSTEINBERG)
else:
threshold = 100.0 - threshold
threshold = min(255, max(0, int(threshold/100.0 * 255))) # from percent to pixel val
im = im.point(lambda x: 0 if x < threshold else 255, mode="1")
try:
qlr.add_switch_mode()
@@ -142,6 +162,7 @@ def create_label(qlr, image, label_size, threshold=70, cut=True, dither=False, c
try:
qlr.dpi_600 = False
qlr.cut_at_end = cut
qlr.two_color_printing = True if red else False
qlr.add_expanded_mode()
except BrotherQLUnsupportedCmd:
pass
@@ -150,7 +171,10 @@ def create_label(qlr, image, label_size, threshold=70, cut=True, dither=False, c
if compress: qlr.add_compression(True)
except BrotherQLUnsupportedCmd:
pass
qlr.add_raster_data(im)
if red:
qlr.add_raster_data(black_im, red_im)
else:
qlr.add_raster_data(im)
qlr.add_print()
if __name__ == "__main__":
+49
View File
@@ -0,0 +1,49 @@
from PIL import Image
import colorsys
def HLSColor(img):
""" https://stackoverflow.com/a/22237709/183995 """
if isinstance(img,Image.Image):
img = img.convert('RGB')
r,g,b = img.split()
Hdat = []
Ldat = []
Sdat = []
for rd,gn,bl in zip(r.getdata(),g.getdata(),b.getdata()) :
h,l,s = colorsys.rgb_to_hls(rd/255.,gn/255.,bl/255.)
Hdat.append(int(h*255.))
Ldat.append(int(l*255.))
Sdat.append(int(s*255.))
r.putdata(Hdat)
g.putdata(Ldat)
b.putdata(Sdat)
return Image.merge('RGB',(r,g,b))
else:
return None
def filtered_hls(im, filter_h, filter_l, filter_s, default_col=(255,255,255)):
hls_im = HLSColor(im)
H, L, S = 0, 1, 2
hls = hls_im.split()
mask_h = hls[H].point(filter_h)
mask_l = hls[L].point(filter_l)
mask_s = hls[S].point(filter_s)
Mdat = []
# for debugging:
#mask_h, mask_l, mask_s = hls_im.split()
seen = []
for h, l, s in zip(mask_h.getdata(), mask_l.getdata(), mask_s.getdata()):
if (h, l, s) not in seen:
seen.append((h, l, s))
#print((h, l, s))
Mdat.append(255 if (h and l and s) else 0)
mask = mask_h
mask.putdata(Mdat)
filtered_im = Image.new("RGB", hls_im.size, color=default_col)
filtered_im.paste(im, None, mask)
return filtered_im
+26 -12
View File
@@ -38,6 +38,7 @@ class BrotherQLRaster(object):
self.page_number = 0
self.cut_at_end = True
self.dpi_600 = False
self.two_color_printing = False
self._compression = False
self.exception_on_warning = False
@@ -153,6 +154,7 @@ class BrotherQLRaster(object):
flags = 0x00
flags |= self.cut_at_end << 3
flags |= self.dpi_600 << 6
flags |= self.two_color_printing << 0
self.data += bytes([flags])
def add_margins(self, dots=0x23):
@@ -174,27 +176,39 @@ class BrotherQLRaster(object):
nbpr = number_bytes_per_row['default']
return nbpr*8
def add_raster_data(self, image):
def add_raster_data(self, image, second_image=None):
""" image: Pillow Image() """
logger.info("raster_image_size: {0}x{1}".format(*image.size))
image = image.transpose(Image.FLIP_LEFT_RIGHT)
image = image.convert("1")
if image.size[0] != self.get_pixel_width():
fmt = 'Wrong pixel width: {}, expected {}'
raise BrotherQLRasterError(fmt.format(image.size[0], self.get_pixel_width()))
frame = bytes(image.tobytes(encoder_name='raw'))
frame_len = len(frame)
row_len = image.size[0]//8
images = [image]
if second_image:
if image.size != second_image.size:
fmt = "First and second image don't have the same dimesions: {} vs {}."
raise BrotherQLRasterError(fmt.format(image.size, second_image.size))
images.append(second_image)
frames = []
for image in images:
image = image.transpose(Image.FLIP_LEFT_RIGHT)
image = image.convert("1")
frames.append(bytes(image.tobytes(encoder_name='raw')))
frame_len = len(frames[0])
row_len = images[0].size[0]//8
start = 0
file_str = BytesIO()
while start + row_len <= frame_len:
row = frame[start:start+row_len]
for i, frame in enumerate(frames):
row = frame[start:start+row_len]
if second_image:
file_str.write(b'\x77\x01' if i == 0 else b'\x77\x02')
else:
file_str.write(b'\x67\x00')
if self._compression:
row = packbits.encode(row)
file_str.write(bytes([len(row)]))
file_str.write(row)
start += row_len
file_str.write(b'\x67\x00')
if self._compression:
row = packbits.encode(row)
file_str.write(bytes([len(row)]))
file_str.write(row)
self.data += file_str.getvalue()
def add_print(self, last_page=True):