110 lines
3.4 KiB
Python
110 lines
3.4 KiB
Python
try:
|
|
import barcode
|
|
from barcode.writer import BaseWriter, mm2px
|
|
except ImportError as e: # pragma: nocover
|
|
raise ImportError(
|
|
"pyhanko.pdf_utils.barcodes requires pyHanko to be installed with "
|
|
"the [image-support] option. You can install missing "
|
|
"dependencies by running "
|
|
"\"pip install 'pyHanko[image-support]'\".",
|
|
e,
|
|
)
|
|
|
|
from pyhanko.pdf_utils.content import PdfContent
|
|
from pyhanko.pdf_utils.layout import BoxConstraints
|
|
from pyhanko.pdf_utils.misc import rd
|
|
|
|
__all__ = ['BarcodeBox', 'PdfStreamBarcodeWriter']
|
|
|
|
|
|
def barcode_colour_to_pdf(colour) -> bytes:
|
|
# TODO there has to be an index of common colour names somewhere, use
|
|
# that instead.
|
|
# default to black
|
|
return b"1 1 1" if colour == "white" else b"0 0 0"
|
|
|
|
|
|
# convert mm to PDF user units
|
|
# 72 PDF user units to an inch is the standard
|
|
PDF_UUPI = 72
|
|
|
|
|
|
# ...so one "pixel" in python-barcode is one PDF user unit
|
|
def mm2uu(mm_len):
|
|
return rd((mm_len / 25.4) * PDF_UUPI)
|
|
|
|
|
|
class PdfStreamBarcodeWriter(BaseWriter):
|
|
"""
|
|
Implementation of writer class for the python-barcode library to output
|
|
PDF graphics operators.
|
|
Note: _paint_text is intentionally dummied out.
|
|
Please use the functionality implemented in pyhanko.pdf_utils.text instead.
|
|
"""
|
|
|
|
def __init__(self):
|
|
def dummy(*_args, **_kwargs):
|
|
pass
|
|
|
|
# The architecture of the BaseWriter class is a little bizarre IMO.
|
|
# It's not clear to me why the author didn't just put in some abstract
|
|
# methods instead, but let's roll with it.
|
|
BaseWriter.__init__(
|
|
self, self._init, self._paint_module, dummy, self._finish
|
|
)
|
|
self._command_stream = None
|
|
|
|
def _init(self, code):
|
|
width, height = self.calculate_size(len(code[0]), len(code))
|
|
self.size = (int(mm2px(width, PDF_UUPI)), int(mm2px(height, PDF_UUPI)))
|
|
self._command_stream = [b'q']
|
|
|
|
def _paint_module(self, xpos, ypos, width, color):
|
|
self._command_stream.append(
|
|
b'%s rg %g %g %g %g re f'
|
|
% (
|
|
barcode_colour_to_pdf(color),
|
|
mm2uu(xpos),
|
|
mm2uu(ypos),
|
|
mm2uu(width),
|
|
mm2uu(self.module_height),
|
|
)
|
|
)
|
|
|
|
def _finish(self) -> bytes:
|
|
self._command_stream.append(b'Q')
|
|
return self.command_stream
|
|
|
|
@property
|
|
def command_stream(self) -> bytes:
|
|
return b'\n'.join(self._command_stream)
|
|
|
|
# we only define this method because python-barcode demands it
|
|
def save(self, filename, output): # pragma: nocover
|
|
pass
|
|
|
|
|
|
class BarcodeBox(PdfContent):
|
|
"""
|
|
Thin wrapper around python-barcode functionality.
|
|
|
|
This will render a barcode of the specified type as PDF graphics operators.
|
|
"""
|
|
|
|
def __init__(self, barcode_type, code):
|
|
self.barcode_type = barcode_type
|
|
self.code = code
|
|
|
|
writer = PdfStreamBarcodeWriter()
|
|
|
|
# render everything here, since we need part of the rendering
|
|
# operation's results to determine the box parameters to pass to
|
|
# the parent
|
|
b = barcode.get_barcode(self.barcode_type, code=code, writer=writer)
|
|
self._barcode_commands = b.render()
|
|
(w, h) = writer.size
|
|
super().__init__(box=BoxConstraints(width=w, height=h))
|
|
|
|
def render(self) -> bytes:
|
|
return self._barcode_commands
|