diff --git a/app/__main__.py b/app/__main__.py index 9dd6da9..6d25f1a 100644 --- a/app/__main__.py +++ b/app/__main__.py @@ -1,10 +1,32 @@ -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): """ @@ -12,11 +34,11 @@ async def print_canvas(canvas: Canvas): """ # 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 @@ -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) @@ -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()) \ No newline at end of file + asyncio.run(main()) diff --git a/web.py b/web.py index 41453f1..5d75e19 100644 --- a/web.py +++ b/web.py @@ -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")