homelog/labelprinter/labels.py

161 lines
6.1 KiB
Python

"""Utilities for label generation.
stolen from: https://github.com/Edinburgh-Genome-Foundry/blabel
"""
import base64
from io import BytesIO
import datetime
import textwrap
from math import floor
import PIL.Image
import barcode as python_barcode
from PIL import Image, ImageDraw, ImageFont
import labelprinter
def now(fmt="%Y-%m-%d %H:%M"):
"""Display the current time.
Default format is "year-month-day hour:minute" but another format can be
provided (see ``datetime`` docs for date formatting).
"""
now = datetime.datetime.now()
if fmt is not None:
now = now.strftime(fmt)
return now
def pil_to_html_imgdata(img, fmt='PNG'):
"""Convert a PIL image into HTML-displayable data.
The result is a string ``data:image/FMT;base64,xxxxxxxxx`` which you
can provide as a "src" parameter to a ``<img/>`` tag.
Examples:
---------
>>> data = pil_to_html_imgdata(my_pil_img)
>>> html_data = '<img src="%s"/>' % data
"""
buffered = BytesIO()
img.save(buffered, format=fmt)
img_str = base64.b64encode(buffered.getvalue())
prefix = 'data:image/%s;charset=utf-8;base64,' % fmt.lower()
return prefix + img_str.decode()
def wrap(text, col_width):
"""Breaks the text into lines with at maximum 'col_width' characters."""
return "\n".join(textwrap.wrap(text, col_width))
def container_label(data, barcode_class='code128', fmt='png', logo_file='./static/images/djlogo256.png',
owner_name='Dirk Jahnke', description='', description_line2='', **options):
"""Return a barcode's image data.
Powered by the Python library ``python-barcode``. See this library's
documentation for more details.
Parameters
----------
data
Data to be encoded in the datamatrix.
barcode_class
Class/standard to use to encode the data. Different standards have
different constraints.
fmt
The format to be used when rendering the resulting image
png -> png image
svg -> embeddable code for html img tag
logo_file
File path to logo file to be included
owner_name
Name of owner printed underneath the logo
description
Text to be printed on bottom of the label
writer_options
Various options for the writer to tune the appearance of the barcode
(see python-barcode documentation).
Returns
-------
image_base64_data
A string ``data:image/png;base64,xxxxxxxxx`` which you can provide as a
"src" parameter to a ``<img/>`` tag.
Examples:
---------
>>> data = barcode('EGF12134', barcode_class='code128')
>>> html_data = '<img src="%s"/>' % data
Examples of writer options:
>>> { 'background': 'white',
>>> 'font_size': 10,
>>> 'foreground': 'black',
>>> 'module_height': 15.0,
>>> 'module_width': 0.2,
>>> 'quiet_zone': 6.5,
>>> 'text': '',
>>> 'text_distance': 5.0,
>>> 'write_text': True
>>> }
"""
with Image.open(logo_file) as logo_img:
font_size = 22
font_size_small = 16
owner_name_font_size = 26
logo_img = logo_img.resize((128, 128))
logo_img = logo_img.convert('L')
threshold = 20
logo_img = logo_img.point(lambda p: 255 if p < threshold else 0)
logo_img = logo_img.convert('1')
constructor = python_barcode.get_barcode_class(barcode_class)
data = str(data).zfill(constructor.digits)
writer = {
'svg': python_barcode.writer.ImageWriter,
'png': python_barcode.writer.ImageWriter,
'image': python_barcode.writer.ImageWriter
}[fmt]
barcode_img = constructor(data, writer=writer())
writer_options = {
'font_size': 24,
'module_width': 0.25,
'module_height': 10,
'quiet_zone': 3,
'text_distance': 1.0
}
writer_options.update(options['writer_options'])
img = barcode_img.render(writer_options=writer_options)
barcode_xsize, barcode_ysize = img.size
logo_xsize, logo_ysize = logo_img.size
label_xsize = 696
label_ysize = barcode_ysize + font_size if description_line2 == '' else barcode_ysize + 2 * font_size
result_img = Image.new('1', (label_xsize, label_ysize), 1)
result_img.paste(logo_img, (owner_name_font_size + 10, floor(barcode_ysize / 2) - 20 - floor(logo_ysize / 2)))
start_x_for_barcode = logo_xsize + 10
max_x_for_barcode = 696 - start_x_for_barcode
result_img.paste(img, (start_x_for_barcode + floor((max_x_for_barcode - barcode_xsize) / 2), 5))
fnt = ImageFont.truetype("./static/fonts/DejaVuSerif.ttf", font_size)
owner_name_fnt = ImageFont.truetype("./static/fonts/DejaVuSerif.ttf", owner_name_font_size)
owner_text_img = Image.new('1', (floor(owner_name_fnt.getlength(owner_name)+1), owner_name_font_size+5), 1)
d = ImageDraw.Draw(owner_text_img)
d.text((0,0), owner_name, font=owner_name_fnt)
owner_text_img = owner_text_img.transpose(PIL.Image.ROTATE_90)
result_img.paste(owner_text_img, (0, 10))
d = ImageDraw.Draw(result_img)
#d.text((20, logo_ysize), owner_name, font=fnt)
if fnt.getlength(description) > 660:
fnt_small = ImageFont.truetype("./static/fonts/DejaVuSerif.ttf", font_size_small)
d.text((0, barcode_ysize), description, font=fnt_small)
if description_line2 != '':
d.text((0, barcode_ysize + font_size), description_line2, font=fnt_small)
else:
#d.text((30 + floor((660 - fnt.getlength(description)) / 2), barcode_ysize), description, font=fnt)
d.text((0, barcode_ysize), description, font=fnt)
if description_line2 != '':
d.text((0, barcode_ysize + font_size), description_line2, font=fnt)
if fmt == 'image':
return result_img
elif fmt == 'png':
return pil_to_html_imgdata(result_img, fmt='PNG')
else:
prefix = "data:image/svg+xml;charset=utf-8;base64,"
return prefix + base64.b64encode(result_img).decode()