Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
*.pyo
*.swp
*.pdf
*.png
*.jpg
__pycache__
.idea/
124 changes: 124 additions & 0 deletions demos/address_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# This file is part of pylabels, a Python library to create PDFs for printing
# labels.
# Copyright (C) 2012, 2013, 2014 Blair Bonnett
#
# pylabels is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# pylabels is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# pylabels. If not, see <http://www.gnu.org/licenses/>.

import os.path

from reportlab.graphics import shapes
from reportlab.lib import colors
from reportlab.pdfbase.pdfmetrics import stringWidth

import labels
from demos.labeldef import build_label_def, build_spec

# Everything in pylabels in in mm, but for row heights we need points
MM2P = 2.83465

# Get the path to the demos directory.
base_path = os.path.dirname(__file__)

# get the label definations
label_defs = build_label_def(os.path.join(base_path, 'labeldef.json'))

# get our spec
spec = build_spec('Avery 5160', label_defs)


# Create a function to draw each label. This will be given the ReportLab drawing
# object to draw on, the dimensions (NB. these will be in points, the unit
# ReportLab uses) of the label, and the name to put on the tag.
def write_address(label, width, height, address):
# first split the address into lines, this is for calculating the length of the longest line
address_lines = address.split('\n')
# get the info
number_lines, line_length = string_size(address)
# find the longest string line
longest_line = address_lines[line_length.index(max(line_length))]
font_size = 12
text_width = width - 10
text_height = height - 10
line_height = (font_size / MM2P) * 1.5
# make sure it fits on a label
longest_width = stringWidth(longest_line, "Times-Roman", font_size)
total_height = number_lines * line_height
while (longest_width > text_width) or (total_height > text_height):
font_size *= .9
longest_width = stringWidth(longest_line, "Times-Roman", font_size)
line_height = (font_size / MM2P) * 1.5
total_height = number_lines * line_height
# now the font is correct, we need to specify the position of the string
# the horizontal part is easy
h_location = width / 2
# now for this algo we are going to define the center as zero to make things easy
# this is no big deal as we can always add the constant to the vector
# first we need to determine if there are an odd or even number of lines
vertical_lines = []
if number_lines % 2 == 0:
# if its even, simply take put n/2 above the center and n/2 below
_l = number_lines // 2
for i in range(_l):
if i == 0:
vertical_lines.append((i + 1) * (line_height * .90))
vertical_lines.append((i + 1) * (-line_height * .90))
else:
vertical_lines.append(((i + 1) * (line_height)) + (line_height * .65))
vertical_lines.append(((i + 1) * (-line_height)) - (line_height * .65))
else:
# odd is a little tricker as the first line gets placed at 0
# but then we need to move from 1/2 the line height up and down
_l = number_lines // 2
vertical_lines.append(0)
_s = line_height / 2.0
for i in range(_l):
_pu = ((i + 1) * -line_height) - _s
_pp = ((i + 1) * line_height) + _s
vertical_lines.append(_pu)
vertical_lines.append(_pp)
# ok now we have a list with the zero based line height positions
# we can simply sort them, and add the scalar
vertical_lines.sort(reverse=True)
vertical_pos = [x + height / 2 for x in vertical_lines]
# after all that, we can finally write the label
for c, line in enumerate(address_lines):
s = shapes.String(h_location, vertical_pos[c], line, textAnchor="middle")
s.fontName = "Times-Roman"
s.fontSize = font_size
s.fillColor = colors.black
label.add(s)


def string_size(address_string):
"""
calculate the number of lines and the length of each line
in that string
:param address_string: the string representing thr address
:return: a tuple with the first number the number of lines and the second a list of line lengths
"""
num_lines = len(address_string.split('\n'))
line_length = [len(line) for line in address_string.split('\n')]
return (num_lines, line_length)


# create the sheet
sheet = labels.Sheet(spec, write_address, border=False)

# lets just write the same address a bunch of times
label_count = (spec.columns * spec.rows)
address = 'Test Name\nTest Address\nTest Address 2\nTest City, Test State, Test Zip'
for i in range(label_count):
sheet.add_label(address)

sheet.save('test.pdf')
print("{0:d} label(s) output on {1:d} page(s).".format(sheet.label_count, sheet.page_count))
30 changes: 30 additions & 0 deletions demos/labeldef.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"label": [
{
"name": "Avery 5160",
"top_margin": 0.5,
"left_margin": 0.19,
"row_gap": 0.0,
"column_gap": 0.12,
"label_height": 1.0,
"label_width": 2.63,
"columns": 3,
"rows": 10,
"page_size": "letter",
"measurement": "in"
},
{
"name": "Avery 5159",
"top_margin": 0.25,
"left_margin": 0.16,
"row_gap": 0.0,
"column_gap": 0.19,
"label_height": 1.5,
"label_width": 4.0,
"columns": 2,
"rows": 7,
"page_size": "letter",
"measurement": "in"
}
]
}
77 changes: 77 additions & 0 deletions demos/labeldef.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# This file is part of pylabels, a Python library to create PDFs for printing
# labels.
# Copyright (C) 2012, 2013, 2014 Blair Bonnett
#
# pylabels is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# pylabels is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# pylabels. If not, see <http://www.gnu.org/licenses/>.


import json

from labels.specifications import Specification

# just some simple static constants
I2MM = 25.4
SHEET_SIZES = {'letter': (8.5, 11), 'A4': (210, 297), 'A5': (148, 210)}


def build_label_def(jsonFilePath):
"""
The basic purpose of this method is to load the JSON file specs
and turn it into a dictionary of dictionaries where the label name
is the key. The dictionary that is the value should then contain
all the necessary parameters to build a specification
:param jsonFilePath:
:return: a dictionary of label specs
"""
with open(jsonFilePath) as json_file:
json_data = json.load(json_file)
d = {}
for label in json_data['label']:
d[label['name']] = label
return d


def build_spec(label_name, spec_dictionary):
"""
Buld a specification from the dictionary we parsed in build_label_def
:param label_name: the label name
:param spec_dictionary: the dictionary of labels
:return: a specification
"""
data = spec_dictionary[label_name]
# this gets the multiplier, if it is defined in inches
# we multiply to get everything into mm
mult = I2MM if data.pop('measurement') == 'in' else 1
data.pop("name")
# lets get the defaults out of the dictionary explicitly
page_size = SHEET_SIZES[data.pop('page_size')]
sheet_width = page_size[0] * mult
sheet_height = page_size[1] * mult
columns = data.pop('columns')
rows = data.pop('rows')
label_width = data.pop('label_width') * mult
label_height = data.pop('label_height') * mult
# now that we have popped all the required stuff out of the dictionary
# we can just use defaults for the rest of the stuff
# here is a list of things that dont get multiplied by the mulitplier
non_mult = ['corner_radius', 'padding_radius', 'background_image', 'background_filename']
# holder for the transformed optional stuff
opt_data = {}
for k in data.keys():
if k in non_mult or not (isinstance(data[k], (int, float))):
opt_data[k] = data[k]
else:
opt_data[k] = data[k] * mult
spec = Specification(sheet_width=sheet_width, sheet_height=sheet_height, columns=columns, rows=rows,
label_height=label_height, label_width=label_width, **opt_data)
return spec