[FIX] hw_drivers: support for TM-U escpos printer models
Before this commit: When printing a receipt with an epson printers models TM-U2X0 (like TM-U220), random characters will be printed instead of the receipt. This happened as a picture of the receipt is sent to the IoT box, but the TM-U2X0 models does not support all "Bit Image commands". We currently use the GS v 0 one which is not supported by this printer models. See: https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=94 In addition, of that, the command itself is obsolete. After this commit: The support for another command "ESC *" is added which is not obsolete and supported by TM-U2X0 models. Notes: - Compared to "GS v 0", the receipt printed with "ESC *" is worst: - It take longer to print (5 seconds vs 1 second) - There is consistently some thin "empty white lines" - Most of the code is inspired from `python-escpos`: https://github.com/python-escpos/python-escpos/blob/master/src/escpos/escpos.py Due to the drawbacks, making it the default way to print would be a bad idea. As such, we can configure the mode (with other parameters) using particular name for the printer (which can be done by adding it manually with cups) opw-3351084,3341907 closes odoo/odoo#131332 X-original-commit: f30085bf673712c205e7ff318961d597297648d0 Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com> Signed-off-by: Loan Sens (lse) <lse@odoo.com>
This commit is contained in:
@@ -194,7 +194,8 @@ class PrinterDriver(Driver):
|
||||
|
||||
return raster_init + raster_page_length + raster_data + raster_close
|
||||
|
||||
def format_escpos(self, im):
|
||||
def format_escpos_bit_image_raster(self, im):
|
||||
""" prints with the `GS v 0`-command """
|
||||
width = int((im.width + 7) / 8)
|
||||
|
||||
raster_send = b'\x1d\x76\x30\x00'
|
||||
@@ -208,7 +209,111 @@ class PrinterDriver(Driver):
|
||||
raster_data += raster_send + width.to_bytes(2, 'little') + slice_height.to_bytes(2, 'little') + im_slice
|
||||
dots = dots[width*max_slice_height:]
|
||||
|
||||
return raster_data + RECEIPT_PRINTER_COMMANDS['escpos']['cut']
|
||||
return raster_data
|
||||
|
||||
def extract_columns_from_picture(self, im, line_height):
|
||||
# Code inspired from python esc pos library:
|
||||
# https://github.com/python-escpos/python-escpos/blob/4a0f5855ef118a2009b843a3a106874701d8eddf/src/escpos/image.py#L73-L89
|
||||
width_pixels, height_pixels = im.size
|
||||
for left in range(0, width_pixels, line_height):
|
||||
box = (left, 0, left + line_height, height_pixels)
|
||||
im_chunk = im.transform(
|
||||
(line_height, height_pixels),
|
||||
Image.EXTENT,
|
||||
box
|
||||
)
|
||||
yield im_chunk.tobytes()
|
||||
|
||||
def format_escpos_bit_image_column(self, im, high_density_vertical=True,
|
||||
high_density_horizontal=True,
|
||||
size_scale=100):
|
||||
""" prints with the `ESC *`-command
|
||||
reference: https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=88
|
||||
|
||||
:param im: PIL image to print
|
||||
:param high_density_vertical: print in high density in vertical direction
|
||||
:param high_density_horizontal: print in high density in horizontal direction
|
||||
:param size_scale: picture scale in percentage,
|
||||
e.g: 50 -> half the size (horizontally and vertically)
|
||||
"""
|
||||
size_scale_ratio = size_scale / 100
|
||||
size_scale_width = int(im.width * size_scale_ratio)
|
||||
size_scale_height = int(im.height * size_scale_ratio)
|
||||
im = im.resize((size_scale_width, size_scale_height))
|
||||
# escpos ESC * command print column per column
|
||||
# (instead of usual row by row).
|
||||
# So we transpose the picture to ease the calculations
|
||||
im = im.transpose(Image.ROTATE_270).transpose(Image.FLIP_LEFT_RIGHT)
|
||||
|
||||
# Most of the code here is inspired from python escpos library
|
||||
# https://github.com/python-escpos/python-escpos/blob/4a0f5855ef118a2009b843a3a106874701d8eddf/src/escpos/escpos.py#L237C9-L251
|
||||
ESC = b'\x1b'
|
||||
density_byte = (1 if high_density_horizontal else 0) + \
|
||||
(32 if high_density_vertical else 0)
|
||||
nL = im.height & 0xFF
|
||||
nH = (im.height >> 8) & 0xFF
|
||||
HEADER = ESC + b'*' + bytes([density_byte, nL, nH])
|
||||
|
||||
raster_data = ESC + b'3\x10' # Adjust line-feed size
|
||||
line_height = 24 if high_density_vertical else 8
|
||||
for column in self.extract_columns_from_picture(im, line_height):
|
||||
raster_data += HEADER + column + b'\n'
|
||||
raster_data += ESC + b'2' # Reset line-feed size
|
||||
return raster_data
|
||||
|
||||
def format_escpos(self, im):
|
||||
# Epson support different command to print pictures.
|
||||
# We use by default "GS v 0", but it is incompatible with certain
|
||||
# printer models (like TM-U2x0)
|
||||
# As we are pretty limited in the information that we have, we will
|
||||
# use the printer name to parse some configuration value
|
||||
# Printer name examples:
|
||||
# EpsonTMM30
|
||||
# -> Print using raster mode
|
||||
# TM-U220__IMC_LDV_LDH_SCALE70__
|
||||
# -> Print using column bit image mode (without vertical and
|
||||
# horizontal density and a scale of 70%)
|
||||
|
||||
# Default image printing mode
|
||||
image_mode = 'raster'
|
||||
|
||||
options_str = self.device_name.split('__')
|
||||
option_str = ""
|
||||
if len(options_str) > 2:
|
||||
option_str = options_str[1].upper()
|
||||
if option_str.startswith('IMC'):
|
||||
image_mode = 'column'
|
||||
|
||||
if image_mode == 'column':
|
||||
# Default printing mode parameters
|
||||
high_density_vertical = True
|
||||
high_density_horizontal = True
|
||||
scale = 100
|
||||
|
||||
# Parse the printer name to get the needed parameters
|
||||
# The separator need to not be filtered by `get_identifier`
|
||||
options = option_str.split('_')
|
||||
for option in options:
|
||||
if option == 'LDV':
|
||||
high_density_vertical = False
|
||||
elif option == 'LDH':
|
||||
high_density_horizontal = False
|
||||
elif option.startswith('SCALE'):
|
||||
scale_value_str = re.search(r'\d+$', option)
|
||||
if scale_value_str is not None:
|
||||
scale = int(scale_value_str.group())
|
||||
else:
|
||||
raise ValueError(
|
||||
"Missing printer SCALE parameter integer "
|
||||
"value in option: " + option)
|
||||
|
||||
res = self.format_escpos_bit_image_column(im,
|
||||
high_density_vertical,
|
||||
high_density_horizontal,
|
||||
scale)
|
||||
else:
|
||||
res = self.format_escpos_bit_image_raster(im)
|
||||
return res + RECEIPT_PRINTER_COMMANDS['escpos']['cut']
|
||||
|
||||
def print_status(self):
|
||||
"""Prints the status ticket of the IoTBox on the current printer."""
|
||||
|
||||
Reference in New Issue
Block a user