paperless-asn-qr-codes/paperless_asn_qr_codes/avery_labels.py

178 lines
5 KiB
Python

from dataclasses import dataclass, KW_ONLY
from collections.abc import Iterator
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import LETTER, A4
from reportlab.lib.units import mm, inch
# Usage:
# label = AveryLabels.AveryLabel(5160)
# label.open( "labels5160.pdf" )
# label.render( RenderAddress, 30 )
# label.close()
#
# 'render' can either pass a callable, which receives the canvas object
# (with X,Y=0,0 at the lower right) or a string "form" name of a form
# previously created with canv.beginForm().
@dataclass
class LabelInfo:
"""Class for modeling label info"""
_: KW_ONLY
labels_horizontal: int
labels_vertical: int
label_size: tuple[float, float]
gutter_size: tuple[float, float]
margin: tuple[float, float]
pagesize: tuple[float, float]
labelInfo: dict[str, LabelInfo] = {
"averyL4731": LabelInfo(
labels_horizontal=7,
labels_vertical=27,
label_size=(25.4 * mm, 10 * mm),
gutter_size=(2.5 * mm, 0),
margin=(9 * mm, 13.5 * mm),
pagesize=A4,
),
# 2.6 x 1 address labels
"avery5160": LabelInfo(
labels_horizontal=3,
labels_vertical=10,
label_size=(187, 72),
gutter_size=(11, 0),
margin=(14, 36),
pagesize=LETTER,
),
"avery5161": LabelInfo(
labels_horizontal=2,
labels_vertical=10,
label_size=(288, 72),
gutter_size=(0, 0),
margin=(18, 36),
pagesize=LETTER,
),
# 4 x 2 address labels
"avery5163": LabelInfo(
labels_horizontal=2,
labels_vertical=5,
label_size=(288, 144),
gutter_size=(0, 0),
margin=(18, 36),
pagesize=LETTER,
),
# 1.75 x 0.5 return address labels
"avery5167": LabelInfo(
labels_horizontal=4,
labels_vertical=20,
label_size=(1.75 * inch, 0.5 * inch),
gutter_size=(0.3 * inch, 0),
margin=(0.3 * inch, 0.5 * inch),
pagesize=LETTER,
),
# 3.5 x 2 business cards
"avery5371": LabelInfo(
labels_horizontal=2,
labels_vertical=5,
label_size=(252, 144),
gutter_size=(0, 0),
margin=(54, 36),
pagesize=LETTER,
),
}
RETURN_ADDRESS = 5167
BUSINESS_CARDS = 5371
class AveryLabel:
def __init__(self, label, debug, **kwargs):
data = labelInfo[label]
self.across = data.labels_horizontal
self.down = data.labels_vertical
self.size = data.label_size
self.labelsep = (
self.size[0] + data.gutter_size[0],
self.size[1] + data.gutter_size[1],
)
self.margins = data.margin
self.topDown = True
self.debug = debug
self.pagesize = data.pagesize
self.position = 0
self.__dict__.update(kwargs)
def open(self, filename):
self.canvas = canvas.Canvas(filename, pagesize=self.pagesize)
if self.debug:
self.canvas.setPageCompression(0)
self.canvas.setLineJoin(1)
self.canvas.setLineCap(1)
def topLeft(self, x=None, y=None):
if x is None:
x = self.position
if y is None:
if self.topDown:
x, y = divmod(x, self.down)
else:
y, x = divmod(x, self.across)
return (
self.margins[0] + x * self.labelsep[0],
self.pagesize[1] - self.margins[1] - (y + 1) * self.labelsep[1],
)
def advance(self):
self.position += 1
if self.position == self.across * self.down:
self.canvas.showPage()
self.position = 0
def close(self):
if self.position:
self.canvas.showPage()
self.canvas.save()
self.canvas = None
# To render, you can either create a template and tell me
# "go draw N of these templates" or provide a callback.
# Callback receives canvas, width, height.
#
# Or, pass a callable and an iterator. We'll do one label
# per iteration of the iterator.
def render(self, thing, count, *args):
assert callable(thing) or isinstance(thing, str)
if isinstance(count, Iterator):
return self.render_iterator(thing, count)
canv = self.canvas
for i in range(count):
canv.saveState()
canv.translate(*self.topLeft())
if self.debug:
canv.setLineWidth(0.25)
canv.rect(0, 0, self.size[0], self.size[1])
if callable(thing):
thing(canv, self.size[0], self.size[1], *args)
elif isinstance(thing, str):
canv.doForm(thing)
canv.restoreState()
self.advance()
def render_iterator(self, func, iterator):
canv = self.canvas
for chunk in iterator:
canv.saveState()
canv.translate(*self.topLeft())
if self.debug:
canv.setLineWidth(0.25)
canv.rect(0, 0, self.size[0], self.size[1])
func(canv, self.size[0], self.size[1], chunk)
canv.restoreState()
self.advance()