mirror of
https://github.com/python-escpos/python-escpos
synced 2025-09-13 09:09:58 +00:00
reformat codebase
This commit is contained in:
@@ -22,17 +22,51 @@ from barcode.writer import ImageWriter
|
||||
|
||||
import os
|
||||
|
||||
from .constants import ESC, GS, NUL, QR_ECLEVEL_L, QR_ECLEVEL_M, QR_ECLEVEL_H, QR_ECLEVEL_Q
|
||||
from .constants import QR_MODEL_1, QR_MODEL_2, QR_MICRO, BARCODE_TYPES, BARCODE_HEIGHT, BARCODE_WIDTH
|
||||
from .constants import (
|
||||
ESC,
|
||||
GS,
|
||||
NUL,
|
||||
QR_ECLEVEL_L,
|
||||
QR_ECLEVEL_M,
|
||||
QR_ECLEVEL_H,
|
||||
QR_ECLEVEL_Q,
|
||||
)
|
||||
from .constants import (
|
||||
QR_MODEL_1,
|
||||
QR_MODEL_2,
|
||||
QR_MICRO,
|
||||
BARCODE_TYPES,
|
||||
BARCODE_HEIGHT,
|
||||
BARCODE_WIDTH,
|
||||
)
|
||||
from .constants import BARCODE_FONT_A, BARCODE_FONT_B, BARCODE_FORMATS
|
||||
from .constants import BARCODE_TXT_OFF, BARCODE_TXT_BTH, BARCODE_TXT_ABV, BARCODE_TXT_BLW
|
||||
from .constants import (
|
||||
BARCODE_TXT_OFF,
|
||||
BARCODE_TXT_BTH,
|
||||
BARCODE_TXT_ABV,
|
||||
BARCODE_TXT_BLW,
|
||||
)
|
||||
from .constants import TXT_SIZE, TXT_NORMAL
|
||||
from .constants import SET_FONT
|
||||
from .constants import LINESPACING_FUNCS, LINESPACING_RESET
|
||||
from .constants import LINE_DISPLAY_OPEN, LINE_DISPLAY_CLEAR, LINE_DISPLAY_CLOSE
|
||||
from .constants import CD_KICK_DEC_SEQUENCE, CD_KICK_5, CD_KICK_2, PAPER_FULL_CUT, PAPER_PART_CUT
|
||||
from .constants import (
|
||||
CD_KICK_DEC_SEQUENCE,
|
||||
CD_KICK_5,
|
||||
CD_KICK_2,
|
||||
PAPER_FULL_CUT,
|
||||
PAPER_PART_CUT,
|
||||
)
|
||||
from .constants import HW_RESET, HW_SELECT, HW_INIT
|
||||
from .constants import CTL_VT, CTL_CR, CTL_FF, CTL_LF, CTL_SET_HT, PANEL_BUTTON_OFF, PANEL_BUTTON_ON
|
||||
from .constants import (
|
||||
CTL_VT,
|
||||
CTL_CR,
|
||||
CTL_FF,
|
||||
CTL_LF,
|
||||
CTL_SET_HT,
|
||||
PANEL_BUTTON_OFF,
|
||||
PANEL_BUTTON_ON,
|
||||
)
|
||||
from .constants import TXT_STYLE
|
||||
from .constants import RT_STATUS_ONLINE, RT_MASK_ONLINE
|
||||
from .constants import RT_STATUS_PAPER, RT_MASK_PAPER, RT_MASK_LOWPAPER, RT_MASK_NOPAPER
|
||||
@@ -50,27 +84,28 @@ from escpos.capabilities import get_profile, BARCODE_B
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class Escpos(object):
|
||||
""" ESC/POS Printer object
|
||||
"""ESC/POS Printer object
|
||||
|
||||
This class is the abstract base class for an esc/pos-printer. The printer implementations are children of this
|
||||
class.
|
||||
"""
|
||||
|
||||
device = None
|
||||
|
||||
def __init__(self, profile=None, magic_encode_args=None, **kwargs):
|
||||
""" Initialize ESCPOS Printer
|
||||
"""Initialize ESCPOS Printer
|
||||
|
||||
:param profile: Printer profile"""
|
||||
self.profile = get_profile(profile)
|
||||
self.magic = MagicEncode(self, **(magic_encode_args or {}))
|
||||
|
||||
def __del__(self):
|
||||
""" call self.close upon deletion """
|
||||
"""call self.close upon deletion"""
|
||||
self.close()
|
||||
|
||||
@abstractmethod
|
||||
def _raw(self, msg):
|
||||
""" Sends raw data to the printer
|
||||
"""Sends raw data to the printer
|
||||
|
||||
This function has to be individually implemented by the implementations.
|
||||
|
||||
@@ -80,14 +115,21 @@ class Escpos(object):
|
||||
pass
|
||||
|
||||
def _read(self):
|
||||
""" Returns a NotImplementedError if the instance of the class doesn't override this method.
|
||||
"""Returns a NotImplementedError if the instance of the class doesn't override this method.
|
||||
:raises NotImplementedError
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def image(self, img_source, high_density_vertical=True, high_density_horizontal=True, impl="bitImageRaster",
|
||||
fragment_height=960, center=False):
|
||||
""" Print an image
|
||||
def image(
|
||||
self,
|
||||
img_source,
|
||||
high_density_vertical=True,
|
||||
high_density_horizontal=True,
|
||||
impl="bitImageRaster",
|
||||
fragment_height=960,
|
||||
center=False,
|
||||
):
|
||||
"""Print an image
|
||||
|
||||
You can select whether the printer should print in high density or not. The default value is high density.
|
||||
When printing in low density, the image will be stretched.
|
||||
@@ -116,14 +158,16 @@ class Escpos(object):
|
||||
im = EscposImage(img_source)
|
||||
|
||||
try:
|
||||
if self.profile.profile_data['media']['width']['pixels'] == "Unknown":
|
||||
print("The media.width.pixel field of the printer profile is not set. " +
|
||||
"The center flag will have no effect.")
|
||||
if self.profile.profile_data["media"]["width"]["pixels"] == "Unknown":
|
||||
print(
|
||||
"The media.width.pixel field of the printer profile is not set. "
|
||||
+ "The center flag will have no effect."
|
||||
)
|
||||
|
||||
max_width = int(self.profile.profile_data['media']['width']['pixels'])
|
||||
max_width = int(self.profile.profile_data["media"]["width"]["pixels"])
|
||||
|
||||
if im.width > max_width:
|
||||
raise ImageWidthError('{} > {}'.format(im.width, max_width))
|
||||
raise ImageWidthError("{} > {}".format(im.width, max_width))
|
||||
|
||||
if center:
|
||||
im.center(max_width)
|
||||
@@ -137,41 +181,59 @@ class Escpos(object):
|
||||
if im.height > fragment_height:
|
||||
fragments = im.split(fragment_height)
|
||||
for fragment in fragments:
|
||||
self.image(fragment,
|
||||
high_density_vertical=high_density_vertical,
|
||||
high_density_horizontal=high_density_horizontal,
|
||||
impl=impl,
|
||||
fragment_height=fragment_height)
|
||||
self.image(
|
||||
fragment,
|
||||
high_density_vertical=high_density_vertical,
|
||||
high_density_horizontal=high_density_horizontal,
|
||||
impl=impl,
|
||||
fragment_height=fragment_height,
|
||||
)
|
||||
return
|
||||
|
||||
if impl == "bitImageRaster":
|
||||
# GS v 0, raster format bit image
|
||||
density_byte = (0 if high_density_horizontal else 1) + (0 if high_density_vertical else 2)
|
||||
header = GS + b"v0" + six.int2byte(density_byte) + self._int_low_high(im.width_bytes, 2) +\
|
||||
self._int_low_high(im.height, 2)
|
||||
density_byte = (0 if high_density_horizontal else 1) + (
|
||||
0 if high_density_vertical else 2
|
||||
)
|
||||
header = (
|
||||
GS
|
||||
+ b"v0"
|
||||
+ six.int2byte(density_byte)
|
||||
+ self._int_low_high(im.width_bytes, 2)
|
||||
+ self._int_low_high(im.height, 2)
|
||||
)
|
||||
self._raw(header + im.to_raster_format())
|
||||
|
||||
if impl == "graphics":
|
||||
# GS ( L raster format graphics
|
||||
img_header = self._int_low_high(im.width, 2) + self._int_low_high(im.height, 2)
|
||||
tone = b'0'
|
||||
colors = b'1'
|
||||
img_header = self._int_low_high(im.width, 2) + self._int_low_high(
|
||||
im.height, 2
|
||||
)
|
||||
tone = b"0"
|
||||
colors = b"1"
|
||||
ym = six.int2byte(1 if high_density_vertical else 2)
|
||||
xm = six.int2byte(1 if high_density_horizontal else 2)
|
||||
header = tone + xm + ym + colors + img_header
|
||||
raster_data = im.to_raster_format()
|
||||
self._image_send_graphics_data(b'0', b'p', header + raster_data)
|
||||
self._image_send_graphics_data(b'0', b'2', b'')
|
||||
self._image_send_graphics_data(b"0", b"p", header + raster_data)
|
||||
self._image_send_graphics_data(b"0", b"2", b"")
|
||||
|
||||
if impl == "bitImageColumn":
|
||||
# ESC *, column format bit image
|
||||
density_byte = (1 if high_density_horizontal else 0) + (32 if high_density_vertical else 0)
|
||||
header = ESC + b"*" + six.int2byte(density_byte) + self._int_low_high(im.width, 2)
|
||||
density_byte = (1 if high_density_horizontal else 0) + (
|
||||
32 if high_density_vertical else 0
|
||||
)
|
||||
header = (
|
||||
ESC
|
||||
+ b"*"
|
||||
+ six.int2byte(density_byte)
|
||||
+ self._int_low_high(im.width, 2)
|
||||
)
|
||||
outp = [ESC + b"3" + six.int2byte(16)] # Adjust line-feed size
|
||||
for blob in im.to_column_format(high_density_vertical):
|
||||
outp.append(header + blob + b"\n")
|
||||
outp.append(ESC + b"2") # Reset line-feed size
|
||||
self._raw(b''.join(outp))
|
||||
self._raw(b"".join(outp))
|
||||
|
||||
def _image_send_graphics_data(self, m, fn, data):
|
||||
"""
|
||||
@@ -182,11 +244,19 @@ class Escpos(object):
|
||||
:param data: Data to send
|
||||
"""
|
||||
header = self._int_low_high(len(data) + 2, 2)
|
||||
self._raw(GS + b'(L' + header + m + fn + data)
|
||||
self._raw(GS + b"(L" + header + m + fn + data)
|
||||
|
||||
def qr(self, content, ec=QR_ECLEVEL_L, size=3, model=QR_MODEL_2,
|
||||
native=False, center=False, impl="bitImageRaster"):
|
||||
""" Print QR Code for the provided string
|
||||
def qr(
|
||||
self,
|
||||
content,
|
||||
ec=QR_ECLEVEL_L,
|
||||
size=3,
|
||||
model=QR_MODEL_2,
|
||||
native=False,
|
||||
center=False,
|
||||
impl="bitImageRaster",
|
||||
):
|
||||
"""Print QR Code for the provided string
|
||||
|
||||
:param content: The content of the code. Numeric data will be more efficiently compacted.
|
||||
:param ec: Error-correction level to use. One of QR_ECLEVEL_L (default), QR_ECLEVEL_M, QR_ECLEVEL_Q or
|
||||
@@ -206,50 +276,60 @@ class Escpos(object):
|
||||
if not 1 <= size <= 16:
|
||||
raise ValueError("Invalid block size (must be 1-16)")
|
||||
if model not in [QR_MODEL_1, QR_MODEL_2, QR_MICRO]:
|
||||
raise ValueError("Invalid QR model (must be one of QR_MODEL_1, QR_MODEL_2, QR_MICRO)")
|
||||
raise ValueError(
|
||||
"Invalid QR model (must be one of QR_MODEL_1, QR_MODEL_2, QR_MICRO)"
|
||||
)
|
||||
if content == "":
|
||||
# Handle edge case by printing nothing.
|
||||
return
|
||||
if not native:
|
||||
# Map ESC/POS error correction levels to python 'qrcode' library constant and render to an image
|
||||
if model != QR_MODEL_2:
|
||||
raise ValueError("Invalid QR model for qrlib rendering (must be QR_MODEL_2)")
|
||||
raise ValueError(
|
||||
"Invalid QR model for qrlib rendering (must be QR_MODEL_2)"
|
||||
)
|
||||
python_qr_ec = {
|
||||
QR_ECLEVEL_H: qrcode.constants.ERROR_CORRECT_H,
|
||||
QR_ECLEVEL_L: qrcode.constants.ERROR_CORRECT_L,
|
||||
QR_ECLEVEL_M: qrcode.constants.ERROR_CORRECT_M,
|
||||
QR_ECLEVEL_Q: qrcode.constants.ERROR_CORRECT_Q
|
||||
QR_ECLEVEL_Q: qrcode.constants.ERROR_CORRECT_Q,
|
||||
}
|
||||
qr_code = qrcode.QRCode(version=None, box_size=size, border=1, error_correction=python_qr_ec[ec])
|
||||
qr_code = qrcode.QRCode(
|
||||
version=None, box_size=size, border=1, error_correction=python_qr_ec[ec]
|
||||
)
|
||||
qr_code.add_data(content)
|
||||
qr_code.make(fit=True)
|
||||
qr_img = qr_code.make_image()
|
||||
im = qr_img._img.convert("RGB")
|
||||
|
||||
# Convert the RGB image in printable image
|
||||
self.text('\n')
|
||||
self.text("\n")
|
||||
self.image(im, center=center, impl=impl)
|
||||
self.text('\n')
|
||||
self.text('\n')
|
||||
self.text("\n")
|
||||
self.text("\n")
|
||||
return
|
||||
|
||||
if center:
|
||||
raise NotImplementedError("Centering not implemented for native QR rendering")
|
||||
raise NotImplementedError(
|
||||
"Centering not implemented for native QR rendering"
|
||||
)
|
||||
|
||||
# Native 2D code printing
|
||||
cn = b'1' # Code type for QR code
|
||||
cn = b"1" # Code type for QR code
|
||||
# Select model: 1, 2 or micro.
|
||||
self._send_2d_code_data(six.int2byte(65), cn, six.int2byte(48 + model) + six.int2byte(0))
|
||||
self._send_2d_code_data(
|
||||
six.int2byte(65), cn, six.int2byte(48 + model) + six.int2byte(0)
|
||||
)
|
||||
# Set dot size.
|
||||
self._send_2d_code_data(six.int2byte(67), cn, six.int2byte(size))
|
||||
# Set error correction level: L, M, Q, or H
|
||||
self._send_2d_code_data(six.int2byte(69), cn, six.int2byte(48 + ec))
|
||||
# Send content & print
|
||||
self._send_2d_code_data(six.int2byte(80), cn, content.encode('utf-8'), b'0')
|
||||
self._send_2d_code_data(six.int2byte(81), cn, b'', b'0')
|
||||
self._send_2d_code_data(six.int2byte(80), cn, content.encode("utf-8"), b"0")
|
||||
self._send_2d_code_data(six.int2byte(81), cn, b"", b"0")
|
||||
|
||||
def _send_2d_code_data(self, fn, cn, data, m=b''):
|
||||
""" Wrapper for GS ( k, to calculate and send correct data length.
|
||||
def _send_2d_code_data(self, fn, cn, data, m=b""):
|
||||
"""Wrapper for GS ( k, to calculate and send correct data length.
|
||||
|
||||
:param fn: Function to use.
|
||||
:param cn: Output code type. Affects available data.
|
||||
@@ -259,28 +339,32 @@ class Escpos(object):
|
||||
if len(m) > 1 or len(cn) != 1 or len(fn) != 1:
|
||||
raise ValueError("cn and fn must be one byte each.")
|
||||
header = self._int_low_high(len(data) + len(m) + 2, 2)
|
||||
self._raw(GS + b'(k' + header + cn + fn + m + data)
|
||||
self._raw(GS + b"(k" + header + cn + fn + m + data)
|
||||
|
||||
@staticmethod
|
||||
def _int_low_high(inp_number, out_bytes):
|
||||
""" Generate multiple bytes for a number: In lower and higher parts, or more parts as needed.
|
||||
"""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)
|
||||
max_input = 256 << (out_bytes * 8) - 1
|
||||
if not 1 <= out_bytes <= 4:
|
||||
raise ValueError("Can only output 1-4 bytes")
|
||||
if not 0 <= inp_number <= max_input:
|
||||
raise ValueError("Number too large. Can only output up to {0} in {1} bytes".format(max_input, out_bytes))
|
||||
outp = b''
|
||||
raise ValueError(
|
||||
"Number too large. Can only output up to {0} in {1} bytes".format(
|
||||
max_input, out_bytes
|
||||
)
|
||||
)
|
||||
outp = b""
|
||||
for _ in range(0, out_bytes):
|
||||
outp += six.int2byte(inp_number % 256)
|
||||
inp_number //= 256
|
||||
return outp
|
||||
|
||||
def charcode(self, code="AUTO"):
|
||||
""" Set Character Code Table
|
||||
"""Set Character Code Table
|
||||
|
||||
Sets the control sequence from ``CHARCODE`` in :py:mod:`escpos.constants` as active. It will be sent with
|
||||
the next text sequence. If you set the variable code to ``AUTO`` it will try to automatically guess the
|
||||
@@ -318,11 +402,23 @@ class Escpos(object):
|
||||
return False
|
||||
|
||||
bounds, regex = BARCODE_FORMATS[bc]
|
||||
return any(bound[0] <= len(code) <= bound[1] for bound in bounds) and re_match(regex, code)
|
||||
return any(bound[0] <= len(code) <= bound[1] for bound in bounds) and re_match(
|
||||
regex, code
|
||||
)
|
||||
|
||||
def barcode(self, code, bc, height=64, width=3, pos="BELOW", font="A",
|
||||
align_ct=True, function_type=None, check=True):
|
||||
""" Print Barcode
|
||||
def barcode(
|
||||
self,
|
||||
code,
|
||||
bc,
|
||||
height=64,
|
||||
width=3,
|
||||
pos="BELOW",
|
||||
font="A",
|
||||
align_ct=True,
|
||||
function_type=None,
|
||||
check=True,
|
||||
):
|
||||
"""Print Barcode
|
||||
|
||||
This method allows to print barcodes. The rendering of the barcode is done by the printer and therefore has to
|
||||
be supported by the unit. By default, this method will check whether your barcode text is correct, that is
|
||||
@@ -407,38 +503,46 @@ class Escpos(object):
|
||||
"""
|
||||
if function_type is None:
|
||||
# Choose the function type automatically.
|
||||
if bc in BARCODE_TYPES['A']:
|
||||
function_type = 'A'
|
||||
if bc in BARCODE_TYPES["A"]:
|
||||
function_type = "A"
|
||||
else:
|
||||
if bc in BARCODE_TYPES['B']:
|
||||
if bc in BARCODE_TYPES["B"]:
|
||||
if not self.profile.supports(BARCODE_B):
|
||||
raise BarcodeTypeError((
|
||||
"Barcode type '{bc} not supported for "
|
||||
"the current printer profile").format(bc=bc))
|
||||
function_type = 'B'
|
||||
raise BarcodeTypeError(
|
||||
(
|
||||
"Barcode type '{bc} not supported for "
|
||||
"the current printer profile"
|
||||
).format(bc=bc)
|
||||
)
|
||||
function_type = "B"
|
||||
else:
|
||||
raise BarcodeTypeError((
|
||||
"Barcode type '{bc} is not valid").format(bc=bc))
|
||||
raise BarcodeTypeError(
|
||||
("Barcode type '{bc} is not valid").format(bc=bc)
|
||||
)
|
||||
|
||||
bc_types = BARCODE_TYPES[function_type.upper()]
|
||||
if bc.upper() not in bc_types.keys():
|
||||
raise BarcodeTypeError((
|
||||
"Barcode '{bc}' not valid for barcode function type "
|
||||
"{function_type}").format(
|
||||
raise BarcodeTypeError(
|
||||
(
|
||||
"Barcode '{bc}' not valid for barcode function type "
|
||||
"{function_type}"
|
||||
).format(
|
||||
bc=bc,
|
||||
function_type=function_type,
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
if check and not self.check_barcode(bc, code):
|
||||
raise BarcodeCodeError((
|
||||
"Barcode '{code}' not in a valid format for type '{bc}'").format(
|
||||
code=code,
|
||||
bc=bc,
|
||||
))
|
||||
raise BarcodeCodeError(
|
||||
("Barcode '{code}' not in a valid format for type '{bc}'").format(
|
||||
code=code,
|
||||
bc=bc,
|
||||
)
|
||||
)
|
||||
|
||||
# Align Bar Code()
|
||||
if align_ct:
|
||||
self._raw(TXT_STYLE['align']['center'])
|
||||
self._raw(TXT_STYLE["align"]["center"])
|
||||
# Height
|
||||
if 1 <= height <= 255:
|
||||
self._raw(BARCODE_HEIGHT + six.int2byte(height))
|
||||
@@ -478,35 +582,47 @@ class Escpos(object):
|
||||
if function_type.upper() == "A":
|
||||
self._raw(NUL)
|
||||
|
||||
def soft_barcode(self, barcode_type, data, impl='bitImageColumn',
|
||||
module_height=5, module_width=0.2, text_distance=1,
|
||||
center=True):
|
||||
def soft_barcode(
|
||||
self,
|
||||
barcode_type,
|
||||
data,
|
||||
impl="bitImageColumn",
|
||||
module_height=5,
|
||||
module_width=0.2,
|
||||
text_distance=1,
|
||||
center=True,
|
||||
):
|
||||
|
||||
image_writer = ImageWriter()
|
||||
|
||||
# Check if barcode type exists
|
||||
if barcode_type not in barcode.PROVIDED_BARCODES:
|
||||
raise BarcodeTypeError(
|
||||
'Barcode type {} not supported by software barcode renderer'
|
||||
.format(barcode_type))
|
||||
"Barcode type {} not supported by software barcode renderer".format(
|
||||
barcode_type
|
||||
)
|
||||
)
|
||||
|
||||
# Render the barcode to a fake file
|
||||
barcode_class = barcode.get_barcode_class(barcode_type)
|
||||
my_code = barcode_class(data, writer=image_writer)
|
||||
|
||||
with open(os.devnull, "wb") as nullfile:
|
||||
my_code.write(nullfile, {
|
||||
'module_height': module_height,
|
||||
'module_width': module_width,
|
||||
'text_distance': text_distance
|
||||
})
|
||||
my_code.write(
|
||||
nullfile,
|
||||
{
|
||||
"module_height": module_height,
|
||||
"module_width": module_width,
|
||||
"text_distance": text_distance,
|
||||
},
|
||||
)
|
||||
|
||||
# Retrieve the Pillow image and print it
|
||||
image = my_code.writer._image
|
||||
self.image(image, impl=impl, center=center)
|
||||
|
||||
def text(self, txt):
|
||||
""" Print alpha-numeric text
|
||||
"""Print alpha-numeric text
|
||||
|
||||
The text has to be encoded in the currently selected codepage.
|
||||
The input text has to be encoded in unicode.
|
||||
@@ -517,7 +633,7 @@ class Escpos(object):
|
||||
txt = six.text_type(txt)
|
||||
self.magic.write(txt)
|
||||
|
||||
def textln(self, txt=''):
|
||||
def textln(self, txt=""):
|
||||
"""Print alpha-numeric text with a newline
|
||||
|
||||
The text has to be encoded in the currently selected codepage.
|
||||
@@ -526,7 +642,7 @@ class Escpos(object):
|
||||
:param txt: text to be printed with a newline
|
||||
:raises: :py:exc:`~escpos.exceptions.TextError`
|
||||
"""
|
||||
self.text('{}\n'.format(txt))
|
||||
self.text("{}\n".format(txt))
|
||||
|
||||
def ln(self, count=1):
|
||||
"""Print a newline or more
|
||||
@@ -535,12 +651,12 @@ class Escpos(object):
|
||||
:raises: :py:exc:`ValueError` if count < 0
|
||||
"""
|
||||
if count < 0:
|
||||
raise ValueError('Count cannot be lesser than 0')
|
||||
raise ValueError("Count cannot be lesser than 0")
|
||||
if count > 0:
|
||||
self.text('\n' * count)
|
||||
self.text("\n" * count)
|
||||
|
||||
def block_text(self, txt, font="0", columns=None):
|
||||
""" Text is printed wrapped to specified columns
|
||||
"""Text is printed wrapped to specified columns
|
||||
|
||||
Text has to be encoded in unicode.
|
||||
|
||||
@@ -552,10 +668,23 @@ class Escpos(object):
|
||||
col_count = self.profile.get_columns(font) if columns is None else columns
|
||||
self.text(textwrap.fill(txt, col_count))
|
||||
|
||||
def set(self, align='left', font='a', bold=False, underline=0, width=1,
|
||||
height=1, density=9, invert=False, smooth=False, flip=False,
|
||||
double_width=False, double_height=False, custom_size=False):
|
||||
""" Set text properties by sending them to the printer
|
||||
def set(
|
||||
self,
|
||||
align="left",
|
||||
font="a",
|
||||
bold=False,
|
||||
underline=0,
|
||||
width=1,
|
||||
height=1,
|
||||
density=9,
|
||||
invert=False,
|
||||
smooth=False,
|
||||
flip=False,
|
||||
double_width=False,
|
||||
double_height=False,
|
||||
custom_size=False,
|
||||
):
|
||||
"""Set text properties by sending them to the printer
|
||||
|
||||
:param align: horizontal position for text, possible values are:
|
||||
|
||||
@@ -596,37 +725,41 @@ class Escpos(object):
|
||||
"""
|
||||
|
||||
if custom_size:
|
||||
if 1 <= width <= 8 and 1 <= height <= 8 and isinstance(width, int) and\
|
||||
isinstance(height, int):
|
||||
size_byte = TXT_STYLE['width'][width] + TXT_STYLE['height'][height]
|
||||
if (
|
||||
1 <= width <= 8
|
||||
and 1 <= height <= 8
|
||||
and isinstance(width, int)
|
||||
and isinstance(height, int)
|
||||
):
|
||||
size_byte = TXT_STYLE["width"][width] + TXT_STYLE["height"][height]
|
||||
self._raw(TXT_SIZE + six.int2byte(size_byte))
|
||||
else:
|
||||
raise SetVariableError()
|
||||
else:
|
||||
self._raw(TXT_NORMAL)
|
||||
if double_width and double_height:
|
||||
self._raw(TXT_STYLE['size']['2x'])
|
||||
self._raw(TXT_STYLE["size"]["2x"])
|
||||
elif double_width:
|
||||
self._raw(TXT_STYLE['size']['2w'])
|
||||
self._raw(TXT_STYLE["size"]["2w"])
|
||||
elif double_height:
|
||||
self._raw(TXT_STYLE['size']['2h'])
|
||||
self._raw(TXT_STYLE["size"]["2h"])
|
||||
else:
|
||||
self._raw(TXT_STYLE['size']['normal'])
|
||||
self._raw(TXT_STYLE["size"]["normal"])
|
||||
|
||||
self._raw(TXT_STYLE['flip'][flip])
|
||||
self._raw(TXT_STYLE['smooth'][smooth])
|
||||
self._raw(TXT_STYLE['bold'][bold])
|
||||
self._raw(TXT_STYLE['underline'][underline])
|
||||
self._raw(TXT_STYLE["flip"][flip])
|
||||
self._raw(TXT_STYLE["smooth"][smooth])
|
||||
self._raw(TXT_STYLE["bold"][bold])
|
||||
self._raw(TXT_STYLE["underline"][underline])
|
||||
self._raw(SET_FONT(six.int2byte(self.profile.get_font(font))))
|
||||
self._raw(TXT_STYLE['align'][align])
|
||||
self._raw(TXT_STYLE["align"][align])
|
||||
|
||||
if density != 9:
|
||||
self._raw(TXT_STYLE['density'][density])
|
||||
self._raw(TXT_STYLE["density"][density])
|
||||
|
||||
self._raw(TXT_STYLE['invert'][invert])
|
||||
self._raw(TXT_STYLE["invert"][invert])
|
||||
|
||||
def line_spacing(self, spacing=None, divisor=180):
|
||||
""" Set line character spacing.
|
||||
"""Set line character spacing.
|
||||
|
||||
If no spacing is given, we reset it to the default.
|
||||
|
||||
@@ -646,16 +779,19 @@ class Escpos(object):
|
||||
|
||||
if divisor not in LINESPACING_FUNCS:
|
||||
raise ValueError("divisor must be either 360, 180 or 60")
|
||||
if (divisor in [360, 180]
|
||||
and (not(0 <= spacing <= 255))):
|
||||
raise ValueError("spacing must be a int between 0 and 255 when divisor is 360 or 180")
|
||||
if divisor == 60 and (not(0 <= spacing <= 85)):
|
||||
raise ValueError("spacing must be a int between 0 and 85 when divisor is 60")
|
||||
if divisor in [360, 180] and (not (0 <= spacing <= 255)):
|
||||
raise ValueError(
|
||||
"spacing must be a int between 0 and 255 when divisor is 360 or 180"
|
||||
)
|
||||
if divisor == 60 and (not (0 <= spacing <= 85)):
|
||||
raise ValueError(
|
||||
"spacing must be a int between 0 and 85 when divisor is 60"
|
||||
)
|
||||
|
||||
self._raw(LINESPACING_FUNCS[divisor] + six.int2byte(spacing))
|
||||
|
||||
def cut(self, mode='FULL', feed=True):
|
||||
""" Cut paper.
|
||||
def cut(self, mode="FULL", feed=True):
|
||||
"""Cut paper.
|
||||
|
||||
Without any arguments the paper will be cut completely. With 'mode=PART' a partial cut will
|
||||
be attempted. Note however, that not all models can do a partial cut. See the documentation of
|
||||
@@ -667,28 +803,28 @@ class Escpos(object):
|
||||
"""
|
||||
|
||||
if not feed:
|
||||
self._raw(GS + b'V' + six.int2byte(66) + b'\x00')
|
||||
self._raw(GS + b"V" + six.int2byte(66) + b"\x00")
|
||||
return
|
||||
|
||||
self.print_and_feed(6)
|
||||
|
||||
mode = mode.upper()
|
||||
if mode not in ('FULL', 'PART'):
|
||||
if mode not in ("FULL", "PART"):
|
||||
raise ValueError("Mode must be one of ('FULL', 'PART')")
|
||||
|
||||
if mode == "PART":
|
||||
if self.profile.supports('paperPartCut'):
|
||||
if self.profile.supports("paperPartCut"):
|
||||
self._raw(PAPER_PART_CUT)
|
||||
elif self.profile.supports('paperFullCut'):
|
||||
elif self.profile.supports("paperFullCut"):
|
||||
self._raw(PAPER_FULL_CUT)
|
||||
elif mode == "FULL":
|
||||
if self.profile.supports('paperFullCut'):
|
||||
if self.profile.supports("paperFullCut"):
|
||||
self._raw(PAPER_FULL_CUT)
|
||||
elif self.profile.supports('paperPartCut'):
|
||||
elif self.profile.supports("paperPartCut"):
|
||||
self._raw(PAPER_PART_CUT)
|
||||
|
||||
def cashdraw(self, pin):
|
||||
""" Send pulse to kick the cash drawer
|
||||
"""Send pulse to kick the cash drawer
|
||||
|
||||
Kick cash drawer on pin 2 or pin 5 according to default parameter.
|
||||
For non default parameter send a decimal sequence i.e. [27,112,48] or [27,112,0,25,255]
|
||||
@@ -707,7 +843,7 @@ class Escpos(object):
|
||||
raise CashDrawerError(err)
|
||||
|
||||
def linedisplay_select(self, select_display=False):
|
||||
""" Selects the line display or the printer
|
||||
"""Selects the line display or the printer
|
||||
|
||||
This method is used for line displays that are daisy-chained between your computer and printer.
|
||||
If you set `select_display` to true, only the display is selected and if you set it to false,
|
||||
@@ -722,7 +858,7 @@ class Escpos(object):
|
||||
self._raw(LINE_DISPLAY_CLOSE)
|
||||
|
||||
def linedisplay_clear(self):
|
||||
""" Clears the line display and resets the cursor
|
||||
"""Clears the line display and resets the cursor
|
||||
|
||||
This method is used for line displays that are daisy-chained between your computer and printer.
|
||||
"""
|
||||
@@ -743,7 +879,7 @@ class Escpos(object):
|
||||
self.linedisplay_select(select_display=False)
|
||||
|
||||
def hw(self, hw):
|
||||
""" Hardware operations
|
||||
"""Hardware operations
|
||||
|
||||
:param hw: hardware action, may be:
|
||||
|
||||
@@ -761,12 +897,12 @@ class Escpos(object):
|
||||
pass
|
||||
|
||||
def print_and_feed(self, n=1):
|
||||
""" Print data in print buffer and feed *n* lines
|
||||
"""Print data in print buffer and feed *n* lines
|
||||
|
||||
if n not in range (0, 255) then ValueError will be raised
|
||||
if n not in range (0, 255) then ValueError will be raised
|
||||
|
||||
:param n: number of n to feed. 0 <= n <= 255. default: 1
|
||||
:raises ValueError: if not 0 <= n <= 255
|
||||
:param n: number of n to feed. 0 <= n <= 255. default: 1
|
||||
:raises ValueError: if not 0 <= n <= 255
|
||||
"""
|
||||
if 0 <= n <= 255:
|
||||
# ESC d n
|
||||
@@ -775,7 +911,7 @@ class Escpos(object):
|
||||
raise ValueError("n must be betwen 0 and 255")
|
||||
|
||||
def control(self, ctl, count=5, tab_size=8):
|
||||
""" Feed control sequences
|
||||
"""Feed control sequences
|
||||
|
||||
:param ctl: string for the following control sequences:
|
||||
|
||||
@@ -797,9 +933,9 @@ class Escpos(object):
|
||||
elif ctl.upper() == "CR":
|
||||
self._raw(CTL_CR)
|
||||
elif ctl.upper() == "HT":
|
||||
if not (0 <= count <= 32 and
|
||||
1 <= tab_size <= 255 and
|
||||
count * tab_size < 256):
|
||||
if not (
|
||||
0 <= count <= 32 and 1 <= tab_size <= 255 and count * tab_size < 256
|
||||
):
|
||||
raise TabPosError()
|
||||
else:
|
||||
# Set tab positions
|
||||
@@ -811,7 +947,7 @@ class Escpos(object):
|
||||
self._raw(CTL_VT)
|
||||
|
||||
def panel_buttons(self, enable=True):
|
||||
""" Controls the panel buttons on the printer (e.g. FEED)
|
||||
"""Controls the panel buttons on the printer (e.g. FEED)
|
||||
|
||||
When enable is set to False the panel buttons on the printer will be disabled. Calling the method with
|
||||
enable=True or without argument will enable the panel buttons.
|
||||
@@ -872,11 +1008,11 @@ class Escpos(object):
|
||||
status = self.query_status(RT_STATUS_PAPER)
|
||||
if len(status) == 0:
|
||||
return 2
|
||||
if (status[0] & RT_MASK_NOPAPER == RT_MASK_NOPAPER):
|
||||
if status[0] & RT_MASK_NOPAPER == RT_MASK_NOPAPER:
|
||||
return 0
|
||||
if (status[0] & RT_MASK_LOWPAPER == RT_MASK_LOWPAPER):
|
||||
if status[0] & RT_MASK_LOWPAPER == RT_MASK_LOWPAPER:
|
||||
return 1
|
||||
if (status[0] & RT_MASK_PAPER == RT_MASK_PAPER):
|
||||
if status[0] & RT_MASK_PAPER == RT_MASK_PAPER:
|
||||
return 2
|
||||
|
||||
|
||||
@@ -913,7 +1049,7 @@ class EscposIO(object):
|
||||
self.autoclose = autoclose
|
||||
|
||||
def set(self, **kwargs):
|
||||
""" Set the printer-parameters
|
||||
"""Set the printer-parameters
|
||||
|
||||
Controls which parameters will be passed to :py:meth:`Escpos.set() <escpos.escpos.Escpos.set()>`.
|
||||
For more information on the parameters see the :py:meth:`set() <escpos.escpos.Escpos.set()>`-methods
|
||||
@@ -929,11 +1065,13 @@ class EscposIO(object):
|
||||
params.update(kwargs)
|
||||
|
||||
if isinstance(text, six.text_type):
|
||||
lines = text.split('\n')
|
||||
lines = text.split("\n")
|
||||
elif isinstance(text, list) or isinstance(text, tuple):
|
||||
lines = text
|
||||
else:
|
||||
lines = ["{0}".format(text), ]
|
||||
lines = [
|
||||
"{0}".format(text),
|
||||
]
|
||||
|
||||
# TODO check unicode handling
|
||||
# TODO flush? or on print? (this should prob rather be handled by the _raw-method)
|
||||
@@ -945,8 +1083,7 @@ class EscposIO(object):
|
||||
self.printer.text("{0}\n".format(line))
|
||||
|
||||
def close(self):
|
||||
""" called upon closing the `with`-statement
|
||||
"""
|
||||
"""called upon closing the `with`-statement"""
|
||||
self.printer.close()
|
||||
|
||||
def __enter__(self, **kwargs):
|
||||
|
Reference in New Issue
Block a user