Skip to content

Instantly share code, notes, and snippets.

@dagee
Forked from timrprobocom/AveryLabels.py
Last active January 22, 2024 17:23
Show Gist options
  • Select an option

  • Save dagee/84fca479935b00fd45eff1ba7bec119a to your computer and use it in GitHub Desktop.

Select an option

Save dagee/84fca479935b00fd45eff1ba7bec119a 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 A4, landscape
from reportlab.lib.units import inch, mm, cm
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().
## labelInfo - Description
# avery: (horizonCount, vertCount (hLabel,wLabel), (hsp, vsp), (xst, yst) ),
# avery = Avery Label - article number
# hCount = number of lables horizontally
# vCount = number of lables vertically
# hLabel = label height
# wLabel = label width
# hsp = horizontal space between labels
# vsp = vertical space between labels
# xst = x start on page
# yst = y start on page
labelInfo = {
# 25,4x10 mm ASN label @ A4 L4731REV
4731: (7, 27, (25.4*mm, 10.0*mm), (2.5*mm, 0.0*mm), (8.5*mm, 13.5*mm) ),
# 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 = A4
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()
@wduda
Copy link

wduda commented Jan 21, 2024

4731: (7, 27, (25.4*mm, 13.5*mm), (2.5*mm, 0*mm), (8.5*mm, 13.5*mm) ),

Shouldn't this be

4731: (7, 27, (25.4*mm, 10*mm), (2.5*mm, 0), (8.5*mm, 13.5*mm) )

I was getting only 21 codes on each page and found this as the culprit.

@dagee
Copy link
Author

dagee commented Jan 21, 2024

4731: (7, 27, (25.4*mm, 13.5*mm), (2.5*mm, 0*mm), (8.5*mm, 13.5*mm) ),

Shouldn't this be

4731: (7, 27, (25.4*mm, 10*mm), (2.5*mm, 0), (8.5*mm, 13.5*mm) )

I was getting only 21 codes on each page and found this as the culprit.

Jupp! I found this too and changed it localy - now the late update here,
even with changes to mm and the description for every part of the labelInfo.
Actually, I guess that there have been several versions of 4731 dimensions
Mine is sold in Germany with A4 paper.
hopefully it fits better now.

thanx for the info

@wduda
Copy link

wduda commented Jan 22, 2024

Mine is from Germany as well :-)

In the meantime, people have turned this into a CLI tool available through pip, and further evolved it. There is also a discussion to add support for this in paperless.

https://git.jcg.re/jcgruenhage/paperless-asn-qr-codes

https://pypi.org/project/paperless-asn-qr-codes/

paperless-ngx/paperless-ngx#4588

https://github.com/gitolicious/avery-asn/

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