Skip to content

Commit

Permalink
Merge branch 'release/0.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
sam159 committed Jun 22, 2024
2 parents 7d1c782 + 81ad033 commit ad98723
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Dockerhub Publish

on:
push:
branches: [ "main" ]
branches: [ "main", "develop" ]
tags: [ "v*" ]
pull_request:
branches: [ "main" ]
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.4.0

- Added support for printing endless labels

# 0.3.0

- Added support for using QR codes instead of Datamatrix
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,15 @@ The label size and printer are configured via environmental variables. You can a
| NAME_MAX_LINES | 4 | The maximum number of lines to use for the name |
| DUE_DATE_FONT | NotoSerif-Regular.ttf | The file name of the font in the fonts directory |
| DUE_DATE_FONT_SIZE | 30 | The size of that font |
| ENDLESS_MARGIN | 10 | The top & bottom margin to add when using endless labels |

Included fonts are `NotoSans-Regular.ttf` and `NotoSerif-Regular.ttf`

## Endless Labels

These are supported, for example the `62` label size. The length of the label will be big enough to accommodate the max number of lines including a margin.
You may want to experiment with font sizes and line count to get the most out of it.

## Endpoints

Two endpoints are available `/print` and `/image` both accept the same parameters. `/image` will return the rendered image as a PNG instead of sending to the printer.
Expand Down Expand Up @@ -75,7 +81,7 @@ Its advisable to run and install in a [venv](https://docs.python.org/3/library/v
python -m venv .venv
source ./.venv/bin/activate
# Install packages
python -m pip install -U -r requirements
python -m pip install -U -r requirements.txt
# exit with ./.venv/bin/deactivate
```
Expand All @@ -84,7 +90,6 @@ For development you can use `flask run --debug` to run the service on port 5000.

## TODO

- Endless Labels
- Some more formatting options

### Docker
Expand All @@ -101,4 +106,4 @@ An example `docker-compose.yml` file can be found [here](docker-compose.yml).

I'll try to keep on top of bugs but feature requests may go unfulfilled. Please use the issue tracking in Github.

PRs welcome!
PRs are welcome!
8 changes: 4 additions & 4 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
NAME_MAX_LINES = int(getenv("NAME_MAX_LINES", "4"))
DUE_DATE_FONT = getenv("NAME_FONT", "NotoSerif-Regular.ttf")
DUE_DATE_FONT_SIZE = int(getenv("DUE_DATE_FONT_SIZE", "30"))
ENDLESS_MARGIN = int(getenv("ENDLESS_MARGIN", "10"))

selected_backend = guess_backend(PRINTER_PATH)
BACKEND_CLASS = backend_factory(selected_backend)['backend_class']
Expand All @@ -33,7 +34,7 @@

@app.route("/")
def home_route():
return "Label %s, Size %ix%i"%(label_spec.identifier, label_spec.dots_printable[0], label_spec.dots_printable[1])
return "Label %s, %s"%(label_spec.identifier, label_spec.name)

def get_params():
source = request.form if request.method == "POST" else request.args
Expand All @@ -57,7 +58,7 @@ def get_params():
def print_route():
(name, barcode, dueDate) = get_params();

label = createLabelImage(label_spec.dots_printable, name, nameFont, NAME_MAX_LINES, createBarcode(barcode, BARCODE_FORMAT), dueDate, ddFont)
label = createLabelImage(label_spec.dots_printable, ENDLESS_MARGIN, name, nameFont, NAME_FONT_SIZE, NAME_MAX_LINES, createBarcode(barcode, BARCODE_FORMAT), dueDate, ddFont)

buf = BytesIO()
label.save(buf, format="PNG")
Expand All @@ -70,7 +71,7 @@ def print_route():
def test():
(name, barcode, dueDate) = get_params();

img = createLabelImage(label_spec.dots_printable, name, nameFont, NAME_MAX_LINES, createBarcode(barcode, BARCODE_FORMAT), dueDate, ddFont)
img = createLabelImage(label_spec.dots_printable, ENDLESS_MARGIN, name, nameFont, NAME_FONT_SIZE, NAME_MAX_LINES, createBarcode(barcode, BARCODE_FORMAT), dueDate, ddFont)
buf = BytesIO()
img.save(buf, format="PNG")
buf.seek(0)
Expand All @@ -89,4 +90,3 @@ def sendToPrinter(image : Image):
be = BACKEND_CLASS(PRINTER_PATH)
be.write(bql.data)
del be

51 changes: 40 additions & 11 deletions app/imaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,62 @@ def createBarcode(text: str, type: str):
case _:
return createDatamatrix(text)

def createLabelImage(labelSize : tuple, text : str, textFont : ImageFont, textMaxLines : int, barcode : Image, dueDate : str, dueDateFont : ImageFont):
def createLabelImage(labelSize : tuple, endlessMargin : int, text : str, textFont : ImageFont, textFontSize : int, textMaxLines : int, barcode : Image, dueDate : str, dueDateFont : ImageFont):
(width, height) = labelSize
# default line spacing used by multiline_text, doesn't seem to have an effect if changed though but we need to take into account
lineSpacing = 4
# margin to use for label
marginTop = 0
marginBottom = 0

# for endless labels with a height of zero
if height == 0:
# height should be text size + spacing x max lines + margin x 2
height = (textFontSize + lineSpacing) * textMaxLines + endlessMargin * 2
# negate the empty space above the text
(_, tTop, _, _) = textFont.getbbox("testing")
marginTop = endlessMargin - tTop
# regular bottom margin
marginBottom = endlessMargin
# make space for the due date
if dueDate:
(_, _, _, ddBottom) = dueDateFont.getbbox(dueDate)
height += ddBottom

# increase the size of the barcode if space permits
if (barcode.size[1] * 4) < labelSize[1]:
if (barcode.size[1] * 8) < height:
barcode = barcode.resize((barcode.size[0] * 8, barcode.size[1] * 8), Image.Resampling.NEAREST)
if (barcode.size[1] * 6) < height:
barcode = barcode.resize((barcode.size[0] * 6, barcode.size[1] * 6), Image.Resampling.NEAREST)
if (barcode.size[1] * 4) < height:
barcode = barcode.resize((barcode.size[0] * 4, barcode.size[1] * 4), Image.Resampling.NEAREST)
if (barcode.size[1] * 2) < labelSize[1]:
if (barcode.size[1] * 2) < height:
barcode = barcode.resize((barcode.size[0] * 2, barcode.size[1] * 2), Image.Resampling.NEAREST)

label = Image.new("RGB", labelSize, ImageColor.getrgb("#FFF"))
# vertically align barcode
label = Image.new("RGB", (width, height), ImageColor.getrgb("#FFF"))
# vertically align barcode (ignoring margin)
barcode_padding = [0, (int)((label.size[1] / 2) - (barcode.size[1] / 2))]
label.paste(barcode, barcode_padding)

draw = ImageDraw.Draw(label)

(nameText, nameTextWidth) = wrapText(text, textFont, label.size[0] - barcode.size[0], textMaxLines)
nameMaxWidth = label.size[0] - barcode.size[0]
(nameText, nameTextWidth) = wrapText(text, textFont, width - barcode.size[0], textMaxLines)
nameMaxWidth = width - barcode.size[0]
nameLeftMargin = (nameMaxWidth - nameTextWidth) / 2

draw.multiline_text(
[barcode.size[0] + nameLeftMargin, 0],
[barcode.size[0] + nameLeftMargin, marginTop],
nameText,
fill = ImageColor.getrgb("#000"),
font = textFont,
align = "center"
align = "center",
spacing = lineSpacing
)

if dueDate:
(_, _, ddRight, ddBottom) = dueDateFont.getbbox(dueDate)
draw.text(
[label.size[0] - ddRight, label.size[1] - ddBottom],
[label.size[0] - ddRight, label.size[1] - ddBottom - marginBottom],
dueDate,
fill = ImageColor.getrgb("#000"),
font = dueDateFont
Expand Down Expand Up @@ -100,5 +126,8 @@ def wrapText(text : str, font : ImageFont, maxWidth : int, maxLines : int):
if len(lines) > maxLines:
lines = lines[0:maxLines]
lines[-1] += '...'
lineLength = font.getlength(lines[-1])
if lineLength > longestLine:
longestLine = lineLength

return ('\n'.join(lines), longestLine)
return ('\n'.join(lines), longestLine)

0 comments on commit ad98723

Please sign in to comment.