Create label image for container

This commit is contained in:
Dirk Jahnke 2022-03-30 10:04:38 +02:00
parent 7013301fbd
commit f507481491
7 changed files with 182 additions and 10 deletions

View File

@ -20,7 +20,7 @@
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<a class="btn btn-primary" href="{% url 'container:edit' container.id %}" role="button">Edit</a> <a class="btn btn-primary" href="{% url 'container:update' container.id %}" role="button">Edit</a>
<a class="btn btn-primary" href="{% url 'container:delete' container.id %}" role="button">Delete</a> <a class="btn btn-primary" href="{% url 'container:delete' container.id %}" role="button">Delete</a>
</div> </div>

View File

@ -29,7 +29,11 @@
<td><div>{{ container.color }}</div></td> <td><div>{{ container.color }}</div></td>
<td><div>{{ container.description }}</div></td> <td><div>{{ container.description }}</div></td>
<td><div>{{ container.changed_ts | date:'H:i:s d.m.Y' }}</div></td> <td><div>{{ container.changed_ts | date:'H:i:s d.m.Y' }}</div></td>
<td><a class="btn btn-outline-primary btn-sm" href="{% url 'container:delete' container.id %}" role="button">Delete</a></td> <td>
<a class="btn btn-outline-primary btn-sm" href="{% url 'container:delete' container.id %}" role="button">Delete</a>
<a class="btn btn-outline-primary btn-sm" href="{% url 'container:detail' container.id %}" role="button">View</a>
<a class="btn btn-outline-primary btn-sm" href="{% url 'container:print_label' container.id %}" role="button">Label</a>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -0,0 +1,22 @@
{% extends "base.html" %}
{% load static %}
{% block title %}Container: {{ container.named_id }}{% endblock %}
{% block content %}
===== Print Label Content =====
<!-- Action buttons -->
<div class="row">
<div class="col-sm-12">
Label created for container {{ container.named_id }} of type {{ container.container_type.named_id }}
<br />
<img class="img-fluid shadow p-3 mb-5 bg-body rounded" src="{{ barcode_img }}">
</div>
</div>
<!-- End Action buttons -->
</div>
{% endblock content %}

View File

@ -1,13 +1,15 @@
from django.urls import path from django.urls import path
from container.views import ContainerListView, ContainerUpdateView, ContainerCreateView, ContainerDetailView, ContainerDeleteView from container.views import ContainerListView, ContainerUpdateView, ContainerCreateView, ContainerDetailView, ContainerDeleteView, ContainerPrintLabelView
from container.views import ContainerTypeListView, ContainerTypeDetailView, ContainerTypeCreateView, ContainerTypeUpdateView, ContainerTypeDeleteView from container.views import ContainerTypeListView, ContainerTypeDetailView, ContainerTypeCreateView, ContainerTypeUpdateView, ContainerTypeDeleteView
app_name = 'container' app_name = 'container'
urlpatterns = [ urlpatterns = [
path('', ContainerListView.as_view(), name='list'), path('', ContainerListView.as_view(), name='list'),
path('<int:pk>/', ContainerDetailView.as_view(), name='detail'),
path('add/', ContainerCreateView.as_view(), name='add'), path('add/', ContainerCreateView.as_view(), name='add'),
path('edit/<int:pk>/', ContainerUpdateView.as_view(), name='update'), path('edit/<int:pk>/', ContainerUpdateView.as_view(), name='update'),
path('label/<int:pk>/', ContainerPrintLabelView.as_view(), name='print_label'),
path('delete/<int:pk>/', ContainerDeleteView.as_view(), name='delete'), path('delete/<int:pk>/', ContainerDeleteView.as_view(), name='delete'),
path('type/', ContainerTypeListView.as_view(), name='container_type_list'), path('type/', ContainerTypeListView.as_view(), name='container_type_list'),
path('type/<int:pk>/', ContainerTypeDetailView.as_view(), name='container_type_detail'), path('type/<int:pk>/', ContainerTypeDetailView.as_view(), name='container_type_detail'),

View File

@ -1,6 +1,7 @@
from django.views import generic from django.views import generic
from .models import Container, ContainerType from .models import Container, ContainerType
from blabel import LabelWriter from labelprinter.labels import container_label
class ContainerListView(generic.ListView): class ContainerListView(generic.ListView):
model = Container model = Container
@ -48,13 +49,36 @@ class ContainerPrintLabelView(generic.DetailView):
model = Container model = Container
template_name = 'container/container_print_label.html' template_name = 'container/container_print_label.html'
def get(self, **kwargs): def get(self, request, **kwargs):
super().get(kwargs) context = super().get(request, **kwargs)
'''
label_writer = LabelWriter( label_writer = LabelWriter(
'templates/label/container_label.html', "container/templates/label/container_label.html",
default_stylesheets=("templates/label/label_style.css",) default_stylesheets=("container/templates/label/label_style.css",)
) )
label_writer.write_labels(kwargs['pk'], target="container_label.pdf") label_writer.write_labels([dict(named_id=kwargs['pk'], id=kwargs['pk'])], target="container_label.pdf")
'''
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
from labelprinter.labels import container_label
container_description = self.object.container_type.named_id
if len(self.object.container_type.description) > 0:
container_description += ': ' + self.object.container_type.description
if len(self.object.description) > 0:
container_description += ' / ' + self.object.description
context['barcode_img'] = container_label(self.object.named_id, description=container_description, writer_options={'background': 'white',
'font_size': 10,
'foreground': 'black',
'module_height': 10.0,
'module_width': 0.2,
'quiet_zone': 2.5,
'text': 'This is the text',
'text_distance': 3.0,
'write_text': True
})
return context
class ContainerTypeListView(generic.ListView): class ContainerTypeListView(generic.ListView):
@ -101,4 +125,3 @@ class ContainerTypeUpdateView(generic.UpdateView):
class ContainerTypeDeleteView(generic.DetailView): class ContainerTypeDeleteView(generic.DetailView):
model = ContainerType model = ContainerType

0
labelprinter/__init__.py Normal file
View File

121
labelprinter/labels.py Normal file
View File

@ -0,0 +1,121 @@
"""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 barcode as python_barcode
from PIL import Image, ImageDraw, ImageFont
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='', **writer_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
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 = 16
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
}[fmt]
barcode_img = constructor(data, writer=writer())
img = barcode_img.render(writer_options=writer_options)
barcode_xsize, barcode_ysize = img.size
logo_xsize, logo_ysize = logo_img.size
result_img = Image.new('1', (696, barcode_ysize+20), 1)
result_img.paste(logo_img, (0, floor(barcode_ysize/3)-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("Pillow/Tests/fonts/FreeMono.ttf", 40)
fnt = ImageFont.truetype("./static/fonts/DejaVuSerif.ttf", font_size)
d = ImageDraw.Draw(result_img)
d.text((20,logo_ysize+40), owner_name, font=fnt)
d.text((30+floor((660-fnt.getlength(description))/2),barcode_ysize), description, font=fnt)
if 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()