Moving to spec modules

This commit is contained in:
Tris Forster 2023-02-07 12:54:05 +11:00
parent ebc088198f
commit 5b09007939
11 changed files with 161 additions and 36 deletions

View File

@ -5,10 +5,27 @@ Generate labels for printing using HTML rendering.
## Features
* Customizable for most label sheet layouts.
* Partial sheet printing
* Custom data for each label
* Uses HTML templates for easy customization
* Uses HTML templates easy label design.
## TODO
## Usage
The simplest use case is some static labels:
python -m pylabel -t examples/simple.html -p avery.L7157 -c 10 > output.html
Then, when you need some more just pop the same sheet back in the printer and offset the start
python -m pylabel -t examples/simple.html -p avery.L7157 -c 10 > output.html
To do some mail merge operations create a list of names:
Andy Andrews
Bob Brown
Charlie Chaplin
and use a `{data[text]}` element in your template. Note we've shifted to an external stylesheet now as well.
python -m pylabel -t examples/name_tags.html -p avery.L7157 -d examples/names.txt -s examples/hello.css > output.html
* Switch to profiles for command line usage
* Switch to

0
examples/__init__.py Normal file
View File

33
examples/hello.css Normal file
View File

@ -0,0 +1,33 @@
DIV.label {
background-color: red;
color: white;
}
DIV.hello-tag {
height: 100%;
display: flex;
text-align: center;
align-items: stretch;
flex-direction: column;
justify-content: space-between;
font-family: Arial;
font-size: 10pt;
}
.greeting {
font-size: 20pt;
font-weight: bold;
}
.name {
font-family: 'Times New Roman', Times, serif;
font-weight: bold;
font-size: 20pt;
border-radius: 10px;
background-color: white;
flex-grow: 1;
margin-bottom: 2mm;
color: #333;
}

5
examples/name_tags.html Normal file
View File

@ -0,0 +1,5 @@
<div class="hello-tag";">
<div class="greeting">Hello</div>
<div>my name is</div>
<div class="name">{data[text]}</div>
</div>

3
examples/names.txt Normal file
View File

@ -0,0 +1,3 @@
Andy Andrews
Bob Brown
Charlie Chaplin

5
examples/simple.html Normal file
View File

@ -0,0 +1,5 @@
<div style="height: 100%; display: flex; align-items: center; flex-direction: column; justify-content: space-around; font-family: Arial; font-size: 10pt;">
<div>Property of</div>
<div style="font-size: 20pt"><strong>Andy Andrews</strong></div>
<div>Please return to office if found</div>
</div>

15
examples/simple.py Normal file
View File

@ -0,0 +1,15 @@
from pylabel import avery
profile = avery.L6009
data = range(10)
template = """
Hello World
"""
style = """
.content {
font-family: Arial;
}
"""

View File

@ -21,7 +21,7 @@ class LabelSheet(object):
logger.debug(profile)
self.size = profile['size']
logger.debug(self.size)
logger.debug("Label size: %r", self.size)
# get the pagesize and rotate if landscape
pagesize = profile.get('pagesize', 'a4')
@ -57,10 +57,20 @@ class LabelSheet(object):
self.layout = layout
# Calculate gaps between labels
pitch = profile.get('pitch')
if pitch:
logger.debug("Pitch: %r", pitch)
self.gaps = (
pitch[0] - self.size[0],
pitch[1] - self.size[1],
)
else:
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
@ -87,7 +97,7 @@ DIV.label {{
def render_page(self, template, data, skip=0):
label_template = f'<div class="label"><div class="content">{template}</div></div>'
label_template = f'<div class="label"><div class="printable"><div class="content">{template}</div></div></div>'
blank_labels = "".join([ '<div class="label"><div class="content">&nbsp;</div></div>' for x in range(skip) ])

View File

@ -3,30 +3,46 @@ import sys
import logging
import importlib
from . import LabelSheet
from pylabel import LabelSheet
class EmptySpec:
profile = None
template = None
data = None
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('--count', '-c', type=int, default=0, help="Number of labels to generate")
parser.add_argument('--skip', '-n', type=int, default=0, help="Skip first n labels")
parser.add_argument('--profile', '-p', help='Label profile')
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('--style', '-s', type=argparse.FileType('r'), help='Additional stylesheet')
parser.add_argument('--data', '-d', type=argparse.FileType('r'), help="Pre-generated data, one entry per line")
parser.add_argument('--json', '-j', action='store_true', help='Parse data lines as json')
parser.add_argument('--logging', '-l', default="info", help="Logging level")
parser.add_argument("spec", nargs="?", help="Specification module")
options = parser.parse_args()
logging.basicConfig(level=getattr(logging, options.logging.upper(), logging.INFO))
logger.debug(options)
if options.spec:
spec = importlib.import_module(options.spec)
else:
spec = EmptySpec
try:
if options.profile:
module, profile = options.profile.rsplit('.', 1)
if module.startswith('avery'):
module = 'pylabel.avery'
m = importlib.import_module(module)
p = getattr(m, profile)
spec.profile = getattr(m, profile)
except ValueError:
raise RuntimeError("Expected profile in the form module.profile")
except ModuleNotFoundError:
@ -34,24 +50,31 @@ except ModuleNotFoundError:
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()
spec.template = options.template.read()
options.template.close()
lines = [ l.strip() for l in sys.stdin.readlines() if l != '\n' ]
if options.style is not None:
spec.style = options.style.read()
options.style.close()
if options.data is not None:
lines = [ l.strip() for l in options.data.readlines() if l != '\n' ]
options.data.close()
if options.json:
import json
data = [ json.loads(line) for line in lines ]
spec.data = [ json.loads(line) for line in lines ]
else:
data = [ {'text': line} for line in lines ]
spec.data = [ {'text': line} for line in lines ]
sys.stdout.write(sheet.render_html(template, data, options.skip, extra_style))
if options.count:
spec.data = range(options.count)
for required in ('profile', 'template', 'data'):
if not getattr(spec, required):
parser.error(f"Missing {required}")
sheet = LabelSheet(spec.profile)
sys.stdout.write(sheet.render_html(spec.template, spec.data, options.skip, getattr(spec, 'style', '')))

View File

@ -1,4 +1,11 @@
L6009 = {
"size": (45.7, 21.2),
"layout": (4, 12),
"papersize": "a4",
"margins": (9.8, 21.4),
}
L7157 = {
"size": (64, 24.3),
"layout": (3, 11),

View File

@ -22,6 +22,11 @@
}
}
HTML, BODY {
margin: 0;
padding: 0;
}
DIV.label {
display: inline-block;
background-color: #FFF;
@ -29,5 +34,7 @@ DIV.label {
}
DIV.content {
margin: 2mm; /* Minimum to be safe */
/*margin: 2mm; /* Minimum to be safe */
height: 100%;
margin: 0 2mm;
}