You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
6 years ago
|
#!/usr/bin/env python
|
||
|
"""
|
||
|
This is a minimal ESC/POS printing script which uses the 'column format'
|
||
|
of image output.
|
||
|
|
||
|
The snippet is designed to efficiently delegate image processing to
|
||
|
PIL, rather than spend CPU cycles looping over pixels.
|
||
|
|
||
|
Do not attempt to use this snippet in production, get a copy of python-escpos instead!
|
||
|
"""
|
||
|
|
||
|
from PIL import Image, ImageOps
|
||
|
import six
|
||
|
import sys
|
||
|
import os
|
||
|
|
||
|
def _to_column_format(im, line_height):
|
||
|
"""
|
||
|
Extract slices of an image as equal-sized blobs of column-format data.
|
||
|
|
||
|
:param im: Image to extract from
|
||
|
:param line_height: Printed line height in dots
|
||
|
"""
|
||
|
width_pixels, height_pixels = im.size
|
||
|
top = 0
|
||
|
left = 0
|
||
|
blobs = []
|
||
|
while left < width_pixels:
|
||
|
remaining_pixels = width_pixels - left
|
||
|
box = (left, top, left + line_height, top + height_pixels)
|
||
|
slice = im.transform((line_height, height_pixels), Image.EXTENT, box)
|
||
|
bytes = slice.tobytes()
|
||
|
blobs.append(bytes)
|
||
|
left += line_height
|
||
|
return blobs
|
||
|
|
||
|
def _int_low_high(inp_number, out_bytes):
|
||
|
""" Generate multiple bytes for a number: In lower and higher parts, or more parts as needed.
|
||
|
|
||
|
:param inp_number: Input number
|
||
|
:param out_bytes: The number of bytes to output (1 - 4).
|
||
|
"""
|
||
|
max_input = (256 << (out_bytes * 8) - 1);
|
||
|
if not 1 <= out_bytes <= 4:
|
||
|
raise ValueError("Can only output 1-4 byes")
|
||
|
if not 0 <= inp_number <= max_input:
|
||
|
raise ValueError("Number too large. Can only output up to {0} in {1} byes".format(max_input, out_bytes))
|
||
|
outp = b'';
|
||
|
for _ in range(0, out_bytes):
|
||
|
outp += six.int2byte(inp_number % 256)
|
||
|
inp_number = inp_number // 256
|
||
|
return outp
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
# Configure
|
||
|
high_density_horizontal = True
|
||
|
high_density_vertical = True
|
||
|
if len(sys.argv) > 1:
|
||
|
filename = sys.argv[1]
|
||
|
else:
|
||
|
filename = u"img.png"
|
||
|
|
||
|
# Load Image
|
||
|
im = Image.open(filename)
|
||
|
|
||
|
# Initial rotate. mirror, and extract blobs for each 8 or 24-pixel row
|
||
|
# Convert to black & white via greyscale (so that bits can be inverted)
|
||
|
im = im.convert("L") # Invert: Only works on 'L' images
|
||
|
im = ImageOps.invert(im) # Bits are sent with 0 = white, 1 = black in ESC/POS
|
||
|
im = im.convert("1") # Pure black and white
|
||
|
im = im.transpose(Image.ROTATE_270).transpose(Image.FLIP_LEFT_RIGHT)
|
||
|
line_height = 3 if high_density_vertical else 1
|
||
|
blobs = _to_column_format (im, line_height * 8);
|
||
|
|
||
|
# Generate ESC/POS header and print image
|
||
|
ESC = b"\x1b";
|
||
|
# Height and width refer to output size here, image is rotated in memory so coordinates are swapped
|
||
|
height_pixels, width_pixels = im.size
|
||
|
density_byte = (1 if high_density_horizontal else 0) + (32 if high_density_vertical else 0);
|
||
|
header = ESC + b"*" + six.int2byte(density_byte) + _int_low_high( width_pixels, 2 );
|
||
|
|
||
|
with os.fdopen(sys.stdout.fileno(), 'wb') as fp:
|
||
|
fp.write(ESC + b"3" + six.int2byte(16)); # Adjust line-feed size
|
||
|
for blob in blobs:
|
||
|
fp.write(header + blob + b"\n")
|
||
|
fp.write(ESC + b"2"); # Reset line-feed size
|
||
|
|