1
0
mirror of https://github.com/python-escpos/python-escpos synced 2025-08-24 09:03:34 +00:00

Add justify to text alignment of software_columns() Fixes #689 (#690)

* Add method: justify

* Add justify test

* Add another justify test

* Please the linter

* Allow single-item text_list in _rearrange_into_cols()

* Add parameter checks to software_columns

* Test for specific errors
This commit is contained in:
Benito López
2025-08-11 01:21:29 +02:00
committed by GitHub
parent b85d5b907d
commit ebd6f88acf
2 changed files with 30 additions and 8 deletions

View File

@@ -11,6 +11,7 @@ This module contains the abstract base class :py:class:`Escpos`.
"""
from __future__ import annotations
import re
import textwrap
import time
import warnings
@@ -108,7 +109,7 @@ SW_BARCODE_NAMES = {
for name in barcode.PROVIDED_BARCODES
}
Alignment = Union[Literal["center", "left", "right"], str]
Alignment = Union[Literal["center", "left", "right", "justify"], str]
class Escpos(object, metaclass=ABCMeta):
@@ -920,7 +921,21 @@ class Escpos(object, metaclass=ABCMeta):
self.text(textwrap.fill(txt, col_count))
@staticmethod
def _justify(txt: str, width: int) -> str:
"""Justify-text on left AND right sides by padding spaces.
code by: Georgina Skibinski https://stackoverflow.com/a/66087666
suggested by agordon @https://github.com/python-escpos/python-escpos/pull/652
"""
prev_txt = txt
while (length := width - len(txt)) > 0:
txt = re.sub(r"(\s+)", r"\1 ", txt, count=length)
if txt == prev_txt:
break
return txt.rjust(width)
def _padding(
self,
text: str,
width: int,
align: Alignment = "center",
@@ -936,6 +951,10 @@ class Escpos(object, metaclass=ABCMeta):
text = f"{text:<{width}}"
elif align == "right":
text = f"{text:>{width}}"
elif align == "justify":
text = self._justify(text, width)
else:
raise ValueError("Expected a valid alignment: center|left|right|justify")
return text
@@ -972,7 +991,7 @@ class Escpos(object, metaclass=ABCMeta):
textwrap.wrap(text, widths[i], break_long_words=False)
for i, text in enumerate(text_list)
]
max_len = max(*[len(text_group) for text_group in wrapped])
max_len = max(0, *[len(text_group) for text_group in wrapped])
text_colums = []
for i in range(max_len):
row = ["" for _ in range(n_cols)]
@@ -1013,6 +1032,9 @@ class Escpos(object, metaclass=ABCMeta):
If the list of alignment items is shorter than the list of strings then
the last alignment of the list will be applied till the last string (column).
"""
if not all([text_list, widths, align]):
raise TypeError("Value can't be of type None")
n_cols = len(text_list)
if isinstance(widths, int):

View File

@@ -33,11 +33,11 @@ def test_add_padding_into_cols(driver) -> None:
"""
output = driver._add_padding_into_cols(
text_list=["col1", "col2", "col3"],
widths=[6, 6, 6],
align=["center", "left", "right"],
text_list=["col1", "col2", "col3", "col 4"],
widths=[6, 6, 6, 6],
align=["center", "left", "right", "justify"],
)
assert output == [" col1 ", "col2 ", " col3"]
assert output == [" col1 ", "col2 ", " col3", "col 4"]
@pytest.mark.parametrize("text_list", ["", [], None])
@@ -55,7 +55,7 @@ def test_software_columns_invalid_args(driver, text_list, widths, align) -> None
bad_args = [bad_text_list, bad_widths, bad_align]
for kwargs in bad_args:
with pytest.raises(Exception):
with pytest.raises((TypeError, ValueError)):
driver.software_columns(**kwargs)
driver.close()
@@ -69,7 +69,7 @@ def test_software_columns_invalid_args(driver, text_list, widths, align) -> None
],
)
@pytest.mark.parametrize("widths", [[10, 10, 10], [10], 30])
@pytest.mark.parametrize("align", [["center", "left", "right"], ["center"], "center"])
@pytest.mark.parametrize("align", [["center", "left", "right"], ["center"], "justify"])
def test_software_columns_valid_args(driver, text_list, widths, align) -> None:
"""
GIVEN a dummy printer object