Skip to content

Instantly share code, notes, and snippets.

@timrprobocom
Last active March 13, 2024 18:18
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save timrprobocom/3946aca8ab75df8267bbf892a427a1b7 to your computer and use it in GitHub Desktop.
Save timrprobocom/3946aca8ab75df8267bbf892a427a1b7 to your computer and use it in GitHub Desktop.
A class to manage printing on Avery labels with ReportLab
import os
from collections.abc import Iterator
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import LETTER, landscape
from reportlab.lib.units import inch
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
# 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().
# labels across
# labels down
# label size w/h
# label gutter across/down
# page margins left/top
labelInfo = {
# 2.6 x 1 address labels
5160: ( 3, 10, (187, 72), (11, 0), (14, 36)),
5161: ( 2, 10, (288, 72), (0, 0), (18, 36)),
# 4 x 2 address labels
5163: ( 2, 5, (288, 144), (0, 0), (18, 36)),
# 1.75 x 0.5 return address labels
5167: ( 4, 20, (126, 36), (0, 0), (54, 36)),
# 3.5 x 2 business cards
5371: ( 2, 5, (252, 144), (0, 0), (54, 36)),
}
RETURN_ADDRESS = 5167
BUSINESS_CARDS = 5371
class AveryLabel:
def __init__(self, label, **kwargs):
data = labelInfo[label]
self.across = data[0]
self.down = data[1]
self.size = data[2]
self.labelsep = self.size[0]+data[3][0], self.size[1]+data[3][1]
self.margins = data[4]
self.topDown = True
self.debug = False
self.pagesize = LETTER
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 == None:
x = self.position
if y == 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()
@MyIgel
Copy link

MyIgel commented May 20, 2023

Hi @timrprobocom what is the license of this snippet?

@timrprobocom
Copy link
Author

Public domain. I impose no requirements.

@MTRNord
Copy link

MTRNord commented Oct 17, 2023

Thanks for this! works well with other companies too :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment