At the root of the json structure, two elements have to be defined:
width and height. All positions and sizes are defined in mm.
-If the element `border` is set to *true*, a thin black border will be
-drawn around the outer edges (for cutting). If the element
-is set to *false*, no border will be printed. If the element is not set
-at all, printing will be controlled from the form field when building the
-badges, and be off when building tickets.
+The element `border` controls borders and cutmarks for the badge.
+If it is set to *border* or to *true*, a thin black border will be
+drawn around the outer edges (for manual cutting).
+
+If `border` is set to *cutmarks* then outside cutmarks will be drawn
+on the badge. These marks will by defaut be *10mm* long, and placed *3mm*
+from the edge of the badge (with the intersection being right at the
+corner of the badge, of course). The length and offset can be overridden
+by setting `cutmark_length` and `cutmark_offset` respectively.
+
+The value of the `border` element can be overridden from the form field
+when building the badges from the web.
+
+if the element `bleed` is set to a value, border and cutmarks will be
+adjusted to bleed this much on each side of the badge. In practive,
+this means adjusting the border/marks inward by this many mm. For the
+resulting badge to be the expected size, the total size of the badge
+(as specified in `width` and `height`) should be increased by *2 *
+bleed* and the badge elements adjusted for that.
If the element `forcebreaks` is set to *true*, a pagebreak will be forced
between each badge, making sure there is only one badge per
except ImportError:
import contextutil
+
+DEFAULT_CUTMARK_LENGTH = 8
+DEFAULT_CUTMARK_OFS = 3
+
alignments = {
'left': TA_LEFT,
'center': TA_CENTER,
self.hAlign = 'CENTER'
def draw(self):
- if self.js.get('border', False):
- self.canv.rect(0, 0, self.width, self.height)
-
for e in self.js['elements']:
if e == {}:
continue
f(e)
else:
raise Exception("Unknown type %s" % e['type'])
+ self._draw_border()
+
+ def _draw_border(self):
+ if self.js.get('border', False) in ('border', True, 1):
+ self.canv.rect(-1, -1, self.width + 2, self.height + 2)
+ elif self.js.get('border', False) == 'cutmarks':
+ cmlength = self.js.get('cutmark_length', DEFAULT_CUTMARK_LENGTH) * mm
+ cmofs = self.js.get('cutmark_offset', DEFAULT_CUTMARK_OFS) * mm
+ cmpos = cmlength + cmofs
+ bleed = self.js.get('bleed', 0) * mm
+
+ # Bottom left
+ self.canv.line(-cmpos + bleed, bleed, -cmofs + bleed, bleed)
+ self.canv.line(bleed, -cmpos + bleed, bleed, -cmofs + bleed)
+ # Bottom right
+ self.canv.line(self.width + cmofs - bleed, bleed, self.width + cmpos - bleed, bleed)
+ self.canv.line(self.width - bleed, -cmpos + bleed, self.width - bleed, -cmofs + bleed)
+ # Top left
+ self.canv.line(-cmpos + bleed, self.height - bleed, -cmofs + bleed, self.height - bleed)
+ self.canv.line(bleed, self.height + cmofs - bleed, bleed, self.height + cmpos - bleed)
+ # Top right
+ self.canv.line(self.width + cmofs - bleed, self.height - bleed, self.width + cmpos - bleed, self.height - bleed)
+ self.canv.line(self.width - bleed, self.height + cmofs - bleed, self.width - bleed, self.height + cmpos - bleed)
def calc_y(self, o):
return self.height - getmm(o, 'y') - getmm(o, 'height')
else:
raise Exception("JSON parse failed.")
- if 'border' not in js:
- js['border'] = self.border
+ # Potentially override border settings
+ if self.border == 0 or self.border == 'none':
+ js['border'] = ''
+ elif self.border == 1 or self.border == 'border':
+ js['border'] = 'border'
+ elif self.border == 2 or self.border == 'cutmarks':
+ js['border'] = 'cutmarks'
+
self.story.append(JinjaFlowable(js, self.staticdir))
if 'forcebreaks' not in js:
if js.get('forcebreaks', False):
self.story.append(PageBreak())
+ self.js = js
+
def render(self, output):
- doc = SimpleDocTemplate(output, pagesize=self.pagesize, leftMargin=10 * mm, topMargin=5 * mm, rightMargin=10 * mm, bottomMargin=5 * mm)
+ leftMargin = 10 * mm
+ topMargin = 5 * mm
+ if self.js.get('border', None) == 'cutmarks':
+ cmsize = self.js.get('cutmark_length', DEFAULT_CUTMARK_LENGTH) * mm + self.js.get('cutmark_offset', DEFAULT_CUTMARK_OFS) * mm
+ topMargin += cmsize
+ leftMargin += cmsize
+
+ doc = SimpleDocTemplate(output, pagesize=self.pagesize, leftMargin=leftMargin, topMargin=topMargin, rightMargin=10 * mm, bottomMargin=5 * mm)
doc.build(self.story)
parser.add_argument('attendeelist', type=str, help='JSON file with attendee list')
parser.add_argument('outputfile', type=str, help='Name of output PDF file')
parser.add_argument('--confjson', type=str, help='JSON representing conference')
- parser.add_argument('--borders', action='store_true', help='Enable borders on written file')
+ parser.add_argument('--borders', choices=['none', 'border', 'cutmarks'], help='Enable borders on written file')
parser.add_argument('--pagebreaks', action='store_true', help='Enable pagebreaks on written file')
parser.add_argument('--fontroot', type=str, help='fontroot for dejavu fonts')
parser.add_argument('--font', type=str, nargs=1, action='append', help='<font name>:<font path>')
style = [
("FONTNAME", (0, 0), (-1, -1), "DejaVu Serif"),
]
- if self.borders:
+ if self.borders == 1:
style.extend([
('GRID', (0, 0), (-1, -1), 1, colors.black),
('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),
format = data['format']
orientation = data['orientation']
pagesize = data.get('pagesize', 'A4')
- borders = data.get('border', None) == "on"
+ borders = int(data.get('border', 0))
+ if borders == 2 and format != 'badge':
+ return HttpResponse("Cutmarks can only be used for badges", content_type='text/plain')
+
+ # Default borders to on for non-badges (for badges, default is read from inside the badge)
+ if borders == -1 and format != 'badge':
+ borders = 1
+
pagebreaks = data.get('pagebreaks', None) == 'on'
extracols = [_f for _f in [x.strip() for x in data['additionalcols'].split(',')] if _f]
ofields = [self.fieldmap[f] for f in (data['orderby1'], data['orderby2'])]