Initial commit

This commit is contained in:
Tris 2021-04-26 14:54:03 +10:00
commit ebc088198f
10 changed files with 264 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.pyc
*.egg-info
build
dist

1
MANIFEST.in Normal file
View File

@ -0,0 +1 @@
recursive-include pylabel/templates *

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# PyLabel
Generate labels for printing using HTML rendering.
## Features
* Customizable for most label sheet layouts.
* Custom data for each label
* Uses HTML templates for easy customization
## TODO
* Switch to profiles for command line usage
* Switch to

113
pylabel/__init__.py Normal file
View File

@ -0,0 +1,113 @@
import os.path
import math
import logging
logger = logging.getLogger(__name__)
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), 'templates')
# load the default style from the template
with open(os.path.join(TEMPLATE_DIR, 'layout.css'), 'r') as f:
LAYOUT_STYLE = f.read()
PAPER_SIZES = {
'A4': (210, 297)
}
class LabelSheet(object):
def __init__(self, profile):
logger.debug(profile)
self.size = profile['size']
logger.debug(self.size)
# get the pagesize and rotate if landscape
pagesize = profile.get('pagesize', 'a4')
if not isinstance(pagesize, (list, tuple)):
pagesize = PAPER_SIZES[pagesize.upper()]
if profile.get('landscape'):
pagesize = (pagesize[1], pagesize[0])
logger.debug("Pagesize: %r", pagesize)
self.pagesize = pagesize
# get the margins
margins = profile['margins']
if len(margins) == 2:
margins += margins
logger.debug("Margins: %r", margins)
self.margins = margins
# Calculate the printable area
self.printarea = (
pagesize[0]-margins[0]-margins[2],
pagesize[1]-margins[1]-margins[3]
)
logger.debug("Print area: %r", self.printarea)
# Calculate rows and cols if not specified
layout = profile.get('layout')
if not layout:
layout = (
int(self.printarea[0] / self.size[0]),
int(self.printarea[1] / self.size[1])
)
logger.debug("Layout: %r", layout)
self.layout = layout
# Calculate gaps between labels
self.gaps = (
round((self.printarea[0] - layout[0]*self.size[0]) / (layout[0]-1), 1),
round((self.printarea[1] - layout[1]*self.size[1]) / (layout[1]-1), 1)
)
logger.debug("Gaps: %r", self.gaps)
@property
def stylesheet(self):
return LAYOUT_STYLE + """
SECTION.page {{
width: {o.pagesize[0]:0.1f}mm;
height: {o.pagesize[1]:0.1f}mm;
padding-top: {o.margins[1]:0.1f}mm;
}}
DIV.labelarea {{
margin-left: {o.margins[0]:0.1f}mm;
}}
DIV.label {{
width: {o.size[0]:0.1f}mm;
height: {o.size[1]:0.1f}mm;
margin-right: {o.gaps[0]:0.1f}mm;
margin-bottom: {o.gaps[1]:0.1f}mm;
}}
""".format(o=self)
def render_page(self, template, data, skip=0):
label_template = f'<div class="label"><div class="content">{template}</div></div>'
blank_labels = "".join([ '<div class="label"><div class="content">&nbsp;</div></div>' for x in range(skip) ])
labels = "".join([ label_template.format(data=x) for x in data ])
return """
<section class="page">
<div class="labelarea">{labels}</div>
</section>
""".format(labels=blank_labels+labels)
def render_html(self, template, data, skip=0, extra_style=""):
page = self.render_page(template, data, skip)
with open(os.path.join(TEMPLATE_DIR, 'default.html'), 'r') as f:
html = f.read()
return html.format(style=self.stylesheet + extra_style, label_sheet=page)
def __str__(self):
return f"<LabelSheet {self.size}, {self.pagesize}>"

57
pylabel/__main__.py Normal file
View File

@ -0,0 +1,57 @@
import argparse
import sys
import logging
import importlib
from . import LabelSheet
logger = logging.getLogger(__name__)
parser = argparse.ArgumentParser(description="Generate a page of labels")
parser.add_argument("profile", help="Profile to load")
parser.add_argument('--style', '-s', type=argparse.FileType('r'), help='Additional stylesheet')
parser.add_argument('--skip', '-n', type=int, default=0, help="Skip first n labels")
parser.add_argument('--template', '-t', type=argparse.FileType('r'), help='Template for label')
parser.add_argument('--json', '-j', action='store_true', help='Parse lines as json')
parser.add_argument('--logging', '-l', default="info", help="Logging level")
options = parser.parse_args()
logging.basicConfig(level=getattr(logging, options.logging.upper(), logging.INFO))
logger.debug(options)
try:
module, profile = options.profile.rsplit('.', 1)
m = importlib.import_module(module)
p = getattr(m, profile)
except ValueError:
raise RuntimeError("Expected profile in the form module.profile")
except ModuleNotFoundError:
raise RuntimeError(f"Unable to find profile module {module}")
except AttributeError:
raise RuntimeError(f"Unable to file profile {profile} in module {module}")
sheet = LabelSheet(p)
extra_style=""
if options.style is not None:
extra_style = options.style.read()
options.style.close()
template = '<b>{data[text]}</b>'
if options.template is not None:
template = options.template.read()
options.template.close()
lines = [ l.strip() for l in sys.stdin.readlines() if l != '\n' ]
if options.json:
import json
data = [ json.loads(line) for line in lines ]
else:
data = [ {'text': line} for line in lines ]
sys.stdout.write(sheet.render_html(template, data, options.skip, extra_style))

7
pylabel/avery.py Normal file
View File

@ -0,0 +1,7 @@
L7157 = {
"size": (64, 24.3),
"layout": (3, 11),
"papersize": "a4",
"margins": (6, 14.5),
}

View File

@ -0,0 +1,11 @@
<html>
<head>
<title>Label Sheet</title>
<style>
{style}
</style>
</head>
<body>
{label_sheet}
</body>
</html>

View File

@ -0,0 +1,33 @@
@media print {
@page { margin: 0; }
SECTION.page {
page-break-after: always;
background-color: transparent !important;
}
* {
-webkit-print-color-adjust: exact !important; /* Chrome, Safari */
color-adjust: exact !important; /*Firefox*/
}
}
@media screen {
SECTION.page {
background-color: #EEE;
margin: 20pt auto 50pt;
box-shadow: 10px 10px 5px grey;
clear: both;
}
}
DIV.label {
display: inline-block;
background-color: #FFF;
border-radius: 2mm;
}
DIV.content {
margin: 2mm; /* Minimum to be safe */
}

21
setup.cfg Normal file
View File

@ -0,0 +1,21 @@
[metadata]
name = pylabel-tf198
version = 0.0.1
author = Tris Forster
author_email = tris@shoddynet.org
description = Generates label sheets for printing
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/tf198/pylabel
project_urls =
Bug Tracker = https://github.com/tf198/pylabel/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: OS Independent
[options]
packages = find:
include_package_data = True
python_requires = >=3.6

3
setup.py Normal file
View File

@ -0,0 +1,3 @@
import setuptools
setuptools.setup()