Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 70 additions & 45 deletions app/__main__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
from dymo_bluetooth import discover_printers, Canvas
import argparse
import asyncio
from io import BytesIO

import barcode
from barcode.writer import ImageWriter
from dymo_bluetooth import Canvas, discover_printers
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import argparse

# Constants for image dimensions and barcode configuration
DEFAULT_IMAGE_WIDTH = 500
DEFAULT_IMAGE_HEIGHT = 35
DEFAULT_FONT_SIZE = 28
MAX_CANVAS_HEIGHT = 31
BARCODE_MODULE_WIDTH = 2.0
BARCODE_QUIET_ZONE = 2


def _draw_image_to_canvas(img: Image.Image) -> Canvas:
"""
Converts a PIL Image to a DYMO Canvas by drawing black pixels.
"""
canvas = Canvas()
for y in range(img.height):
for x in range(img.width):
if img.getpixel((x, y)) == 0: # Black pixel
canvas.set_pixel(x, y, True)
return canvas


async def print_canvas(canvas: Canvas):
"""
Prints a given Canvas object to a DYMO LetraTag 200B printer.
"""
# Get a list of printers.
printers = await discover_printers()
if not len(printers) > 0:
print("no printer found")

if not printers:
print("No printer found")
return

printer = printers[0]

# Get the first discovered printer and print the
Expand All @@ -25,35 +47,32 @@ async def print_canvas(canvas: Canvas):
await printer.print(canvas)
await printer.disconnect()


async def print_text(text_to_print: str, should_print: bool = False):
"""
Generates an image with the given text and prints it to a DYMO LetraTag 200B.
"""
# Create a new image with a white background.
# The size is a bit arbitrary, we'll crop it later.
img = Image.new('1', (500, 35), 1)
img = Image.new("1", (DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT), 1)
draw = ImageDraw.Draw(img)

# Use a default font.
try:
font = ImageFont.truetype("arial.ttf", 28)
font = ImageFont.truetype("arial.ttf", DEFAULT_FONT_SIZE)
except IOError:
font = ImageFont.load_default()

# Draw the text and get its bounding box.
draw.text((0, 0), text_to_print, font=font, fill=0)

# Crop the image to the text.
bbox = img.getbbox()
if bbox:
img = img.crop(bbox)

# Create a Canvas and draw the image on it.
canvas = Canvas()
for y in range(img.height):
for x in range(img.width):
if img.getpixel((x, y)) == 0: # Black pixel
canvas.set_pixel(x, y, True)
canvas = _draw_image_to_canvas(img)

if should_print:
await print_canvas(canvas)
Expand All @@ -64,61 +83,67 @@ async def generate_and_print_barcode(text_to_encode: str, should_print: bool = F
Generates a barcode for the given text and prints it to a DYMO LetraTag 200B.
"""
# Generate a barcode as a PNG image in memory.
code128 = barcode.get_barcode_class('code128')
# Configure the writer to create a wider barcode with a quiet zone for better scanning.
writer = ImageWriter(format='PNG')
code128 = barcode.get_barcode_class("code128")
# Configure the writer to create a wider barcode with a quiet zone for better
# scanning.
writer = ImageWriter(format="PNG")
barcode_instance = code128(text_to_encode, writer=writer)

# Define options for the barcode image. 'module_width' makes the bars thicker.

options = {
'module_width': 2.0, # Controls the width of the thinnest bar in mm
'quiet_zone': 2, # Margin on the left and right of the barcode
'write_text': False, # Do not write the text representation below the barcode
# Controls the width of the thinnest bar in mm
"module_width": BARCODE_MODULE_WIDTH,
# Margin on the left and right of the barcode
"quiet_zone": BARCODE_QUIET_ZONE,
# Do not write the text representation below the barcode
"write_text": False,
}
buffer = BytesIO()
barcode_instance.write(buffer, options)

# Save the barcode to a file so you can inspect it.
with open("barcode.png", "wb") as f:
f.write(buffer.getvalue())

# Open the image with Pillow.
buffer.seek(0)
img = Image.open(buffer)

# Resize the image to fit the canvas height limit, maintaining aspect ratio.
max_height = 31
if img.height > max_height:
if img.height > MAX_CANVAS_HEIGHT:
aspect_ratio = img.width / img.height
new_height = max_height
new_height = MAX_CANVAS_HEIGHT
new_width = int(aspect_ratio * new_height)
img = img.resize((new_width, new_height), Image.NEAREST)

# Convert the image to 1-bit black and white.
img = img.convert('1')
img = img.convert("1")

# Create a Canvas and draw the barcode on it.
canvas = Canvas()
for y in range(img.height):
for x in range(img.width):
if img.getpixel((x, y)) == 0: # Black pixel
canvas.set_pixel(x, y, True)
canvas = _draw_image_to_canvas(img)

if should_print:
await print_canvas(canvas)


async def main():
parser = argparse.ArgumentParser(description="Generate and print a barcode on a DYMO LetraTag 200B.")
parser.add_argument('text', nargs='?', default="1234567890",
help='The text to encode in the barcode. Defaults to "1234567890".')
parser.add_argument('--type', choices=['text', 'barcode'], default='barcode',
help='The type of print to generate.')
parser = argparse.ArgumentParser(
description="Generate and print a barcode on a DYMO LetraTag 200B."
)
parser.add_argument(
"text",
nargs="?",
default="1234567890",
help='The text to encode in the barcode. Defaults to "1234567890".',
)
parser.add_argument(
"--type",
choices=["text", "barcode"],
default="barcode",
help="The type of print to generate.",
)
args = parser.parse_args()
if args.type == 'barcode':

if args.type == "barcode":
await generate_and_print_barcode(args.text, should_print=True)
else:
await print_text(args.text, should_print=True)


if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main())
32 changes: 18 additions & 14 deletions web.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
from flask import Flask, render_template, request, flash, redirect, url_for, get_flashed_messages
import asyncio
from app.__main__ import generate_and_print_barcode, print_text
from random import randbytes

from flask import Flask, flash, redirect, render_template, request, url_for

from app.__main__ import generate_and_print_barcode, print_text

app = Flask(__name__)
app.secret_key = randbytes(5).hex()

@app.route('/')

@app.route("/")
def index():
return render_template('index.html')
return render_template("index.html")

@app.route('/print', methods=['POST'])

@app.route("/print", methods=["POST"])
async def print_label():
text_to_print = request.form['text']
print_type = request.form['type']
if print_type == 'barcode':
text_to_print = request.form["text"]
print_type = request.form["type"]

if print_type == "barcode":
await generate_and_print_barcode(text_to_print, should_print=True)
flash("Printing barcode...")
else:
await print_text(text_to_print, should_print=True)
flash("Printing text...")

return redirect(url_for('index'))

if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
return redirect(url_for("index"))


if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")