Fix planche generation issues
This commit is contained in:
parent
6a05e46946
commit
d706ae1645
@ -1,6 +1,6 @@
|
|||||||
FROM alpine
|
FROM alpine:3.18
|
||||||
|
|
||||||
RUN apk --no-cache add python3 inkscape bash imagemagick ghostscript ttf-opensans
|
RUN apk --no-cache add python3 inkscape bash imagemagick ghostscript font-droid
|
||||||
RUN apk --no-cache add py3-pip && pip3 install --break-system-packages django tzdata gunicorn
|
RUN apk --no-cache add py3-pip && pip3 install --break-system-packages django tzdata gunicorn
|
||||||
|
|
||||||
ADD backend /zetikettes
|
ADD backend /zetikettes
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -23,7 +23,7 @@
|
|||||||
"model": "tikette.tisub",
|
"model": "tikette.tisub",
|
||||||
"pk": 4,
|
"pk": 4,
|
||||||
"fields": {
|
"fields": {
|
||||||
"name": "qty",
|
"name": "q",
|
||||||
"descritpion": "Poids net (g)",
|
"descritpion": "Poids net (g)",
|
||||||
"default": "370",
|
"default": "370",
|
||||||
"type": "ST"
|
"type": "ST"
|
||||||
@ -43,7 +43,7 @@
|
|||||||
"model": "tikette.tisub",
|
"model": "tikette.tisub",
|
||||||
"pk": 6,
|
"pk": 6,
|
||||||
"fields": {
|
"fields": {
|
||||||
"name": "fruit",
|
"name": "f",
|
||||||
"descritpion": "Quantité de fruits pour 100g (g)",
|
"descritpion": "Quantité de fruits pour 100g (g)",
|
||||||
"default": "60",
|
"default": "60",
|
||||||
"type": "ST"
|
"type": "ST"
|
||||||
|
@ -50,7 +50,7 @@ class Tikette(models.Model):
|
|||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
color = models.CharField(max_length=6)
|
color = models.CharField(max_length=6)
|
||||||
ab = models.CharField(max_length=7, choices=AbVisibility)
|
ab = models.CharField(max_length=7, choices=AbVisibility)
|
||||||
# designation_fontsize is hardcoded to 42.6667
|
# designation_fontsize is hardcoded to 42.6667 in planche/generate.py
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 210 297"
|
|
||||||
height="297mm"
|
|
||||||
width="210mm">
|
|
||||||
<g transform="translate(3, 4.5)">
|
|
||||||
<g transform="translate(-42,144) rotate(-90,144,0)">
|
|
||||||
<g transform="translate( 0,0)">$right0</g>
|
|
||||||
<g transform="translate( 48,0)">$right1</g>
|
|
||||||
<g transform="translate( 96,0)">$right2</g>
|
|
||||||
<g transform="translate(144,0)">$right3</g>
|
|
||||||
<g transform="translate(192,0)">$right4</g>
|
|
||||||
<g transform="translate(240,0)">$right5</g>
|
|
||||||
</g>
|
|
||||||
<g transform="translate(-42,144) rotate(90,144,0)">
|
|
||||||
<g transform="translate( 0,0)">$left0</g>
|
|
||||||
<g transform="translate( 48,0)">$left1</g>
|
|
||||||
<g transform="translate( 96,0)">$left2</g>
|
|
||||||
<g transform="translate(144,0)">$left3</g>
|
|
||||||
<g transform="translate(192,0)">$left4</g>
|
|
||||||
<g transform="translate(240,0)">$left5</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 896 B |
0
backend/zetikettes/tikette/planche/__init__.py
Normal file
0
backend/zetikettes/tikette/planche/__init__.py
Normal file
@ -7,6 +7,8 @@ from .makeplanche import makeplanche
|
|||||||
|
|
||||||
OUT_DIR = tempfile.gettempdir()
|
OUT_DIR = tempfile.gettempdir()
|
||||||
DEFAULT_DPI = 300
|
DEFAULT_DPI = 300
|
||||||
|
DESIGNATION_FONTSIZE = 42.6667
|
||||||
|
|
||||||
|
|
||||||
def inkscapize(svg_in, pdf_out):
|
def inkscapize(svg_in, pdf_out):
|
||||||
png_out = tempfile.mktemp(suffix='.png')
|
png_out = tempfile.mktemp(suffix='.png')
|
||||||
@ -22,31 +24,31 @@ def inkscapize(svg_in, pdf_out):
|
|||||||
os.unlink(png_out)
|
os.unlink(png_out)
|
||||||
|
|
||||||
|
|
||||||
def generate(request, out_dir):
|
def generate(template, subs, out_dir, landscape=False):
|
||||||
""" Generate a sticker sheet.
|
""" Generate a sticker sheet.
|
||||||
|
|
||||||
request: dict-like with the following fields:
|
template: file name for the sticker template
|
||||||
sticker: mandatory. filename for the sticker
|
subs: dict-like with key-value template subtitution fields
|
||||||
subs: mandatory. dict-like with key-value template subtitution fields
|
|
||||||
landscape: optional. defaults to False
|
|
||||||
|
|
||||||
out_dir: you get it
|
out_dir: you get it
|
||||||
|
landscape: optional. defaults to False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# default designation font size
|
||||||
|
subs['designation_fontsize'] = subs.get('designation_fontsize',
|
||||||
|
DESIGNATION_FONTSIZE)
|
||||||
|
|
||||||
# fill in sticker details
|
# fill in sticker details
|
||||||
sticker_out = tempfile.mktemp(suffix='.svg')
|
sticker_out = tempfile.mktemp(suffix='.svg')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(sticker_out, 'w') as stickout:
|
with open(sticker_out, 'w') as stickout:
|
||||||
landscape = request.get('landscape', False)
|
makesticker(os.path.join(out_dir, template), stickout, subs)
|
||||||
makesticker(os.path.join(out_dir, request['sticker']), stickout,
|
|
||||||
request['subs'], landscape=landscape)
|
|
||||||
|
|
||||||
# make sticker sheet
|
# make sticker sheet
|
||||||
planche_out = tempfile.mktemp(suffix='.svg')
|
planche_out = tempfile.mktemp(suffix='.svg')
|
||||||
with open(sticker_out, 'r') as stickin:
|
with open(sticker_out, 'r') as stickin:
|
||||||
with open(planche_out, 'w') as planchout:
|
with open(planche_out, 'w') as planchout:
|
||||||
makeplanche(stickin, planchout)
|
makeplanche(stickin, planchout, landscape=landscape)
|
||||||
|
|
||||||
# process to printable pdf
|
# process to printable pdf
|
||||||
pdf_out = tempfile.mktemp(dir=out_dir, suffix='.pdf')
|
pdf_out = tempfile.mktemp(dir=out_dir, suffix='.pdf')
|
@ -20,6 +20,9 @@ def parse_args():
|
|||||||
default=sys.stdout,
|
default=sys.stdout,
|
||||||
type=argparse.FileType('w'),
|
type=argparse.FileType('w'),
|
||||||
help='output path (default: stdout)')
|
help='output path (default: stdout)')
|
||||||
|
parser.add_argument('--landscape',
|
||||||
|
action='store_true',
|
||||||
|
help='input sticker is in landscape orientation')
|
||||||
parser.add_argument('sticker',
|
parser.add_argument('sticker',
|
||||||
type=argparse.FileType('r'),
|
type=argparse.FileType('r'),
|
||||||
default=sys.stdin,
|
default=sys.stdin,
|
||||||
@ -29,7 +32,7 @@ def parse_args():
|
|||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def makeplanche(sticker, out, template=DEFAULT_SHEET_TEMPLATE):
|
def makeplanche(sticker, out, template=DEFAULT_SHEET_TEMPLATE ,landscape=False):
|
||||||
with open(template) as tpl:
|
with open(template) as tpl:
|
||||||
tpl_data = tpl.read()
|
tpl_data = tpl.read()
|
||||||
|
|
||||||
@ -38,19 +41,11 @@ def makeplanche(sticker, out, template=DEFAULT_SHEET_TEMPLATE):
|
|||||||
lines = lines[1:]
|
lines = lines[1:]
|
||||||
sticker_data = ''.join(lines)
|
sticker_data = ''.join(lines)
|
||||||
|
|
||||||
|
rotate = "translate(102, 0) rotate(90)" if not landscape else ""
|
||||||
|
|
||||||
subs = {
|
subs = {
|
||||||
'left0': sticker_data,
|
'sticker': sticker_data,
|
||||||
'left1': sticker_data,
|
'rotate': rotate,
|
||||||
'left2': sticker_data,
|
|
||||||
'left3': sticker_data,
|
|
||||||
'left4': sticker_data,
|
|
||||||
'left5': sticker_data,
|
|
||||||
'right0': sticker_data,
|
|
||||||
'right1': sticker_data,
|
|
||||||
'right2': sticker_data,
|
|
||||||
'right3': sticker_data,
|
|
||||||
'right4': sticker_data,
|
|
||||||
'right5': sticker_data,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.write(Template(tpl_data).substitute(subs))
|
out.write(Template(tpl_data).substitute(subs))
|
@ -22,25 +22,18 @@ def parse_args():
|
|||||||
parser.add_argument('--teneur', '-t', required=False, help='Teneur en sucre')
|
parser.add_argument('--teneur', '-t', required=False, help='Teneur en sucre')
|
||||||
parser.add_argument('--fruit', '-f', required=False, help='Quantité de fruits')
|
parser.add_argument('--fruit', '-f', required=False, help='Quantité de fruits')
|
||||||
parser.add_argument('--size', '-s', required=False, help='Masse de produit')
|
parser.add_argument('--size', '-s', required=False, help='Masse de produit')
|
||||||
parser.add_argument('--landscape',
|
|
||||||
action='store_true',
|
|
||||||
help='input sticker is in landscape orientation')
|
|
||||||
parser.add_argument('sticker', help='path to the sticker template')
|
parser.add_argument('sticker', help='path to the sticker template')
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def makesticker(sticker: str, out: typing.IO, subs: dict, landscape=False):
|
def makesticker(sticker: str, out: typing.IO, subs: dict):
|
||||||
with open(sticker) as fin:
|
with open(sticker) as fin:
|
||||||
lines = fin.readlines()
|
lines = fin.readlines()
|
||||||
if lines[0].startswith('<?xml'):
|
if lines[0].startswith('<?xml'):
|
||||||
lines = lines[1:]
|
lines = lines[1:]
|
||||||
sticker_data = ''.join(lines)
|
sticker_data = ''.join(lines)
|
||||||
|
|
||||||
if landscape:
|
|
||||||
# Rotate the sticker 90 degrees
|
|
||||||
sticker_data = "<g transform=\"rotate(-90,0,0) translate(-102,0)\">{}</g>".format(sticker_data)
|
|
||||||
|
|
||||||
out.write(Template(sticker_data).substitute(subs))
|
out.write(Template(sticker_data).substitute(subs))
|
||||||
|
|
||||||
|
|
||||||
@ -50,8 +43,8 @@ if __name__ == "__main__":
|
|||||||
'dluo': args.pop('dluo'),
|
'dluo': args.pop('dluo'),
|
||||||
'lot': args.pop('lot'),
|
'lot': args.pop('lot'),
|
||||||
'teneur': args.pop('teneur'),
|
'teneur': args.pop('teneur'),
|
||||||
'fruit': args.pop('fruit'),
|
'f': args.pop('fruit'),
|
||||||
'qty': args.pop('quantite'),
|
'q': args.pop('quantite'),
|
||||||
'size': args.pop('size'),
|
'size': args.pop('size'),
|
||||||
}
|
}
|
||||||
args['subs'] = subs
|
args['subs'] = subs
|
24
backend/zetikettes/tikette/planche/planche.svg.in
Normal file
24
backend/zetikettes/tikette/planche/planche.svg.in
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 210 297"
|
||||||
|
height="297mm"
|
||||||
|
width="210mm">
|
||||||
|
<g transform="translate(3, 4.5)">
|
||||||
|
|
||||||
|
<g transform="translate(0, 0) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 48) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 96) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 144) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 192) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 240) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
|
||||||
|
<g transform="rotate(180, 102, 144)">
|
||||||
|
<g transform="translate(0, 0) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 48) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 96) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 144) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 192) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
<g transform="translate(0, 240) ${rotate} scale(0.26458)">${sticker}</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -5,18 +5,34 @@ from django.http import JsonResponse
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from . import generate as stickersheet
|
from .planche import generate as stickersheet
|
||||||
from .models import Tikette, Tikategory
|
from .models import Tikette, Tikategory
|
||||||
|
|
||||||
CORS={'access-control-allow-origin': '*'}
|
CORS={'access-control-allow-origin': '*'}
|
||||||
|
|
||||||
|
|
||||||
|
def quirk_bold_allergens(ingredients):
|
||||||
|
out = []
|
||||||
|
for ing in (x.strip() for x in ingredients.split(',')):
|
||||||
|
if ing.startswith('*'):
|
||||||
|
out.append(f'<tspan style="font-weight:bold">{ing[1:]}</tspan>')
|
||||||
|
else:
|
||||||
|
out.append(ing)
|
||||||
|
return ", ".join(out)
|
||||||
|
|
||||||
|
|
||||||
def get_list(request):
|
def get_list(request):
|
||||||
tikettes = [{
|
tikettes = [{
|
||||||
'id': x.id,
|
'id': x.id,
|
||||||
'title': x.title,
|
'title': x.title,
|
||||||
'category': x.category.name,
|
'category': x.category.name,
|
||||||
|
'prototempalte': x.category.prototempalte.name,
|
||||||
'landscape': x.category.landscape,
|
'landscape': x.category.landscape,
|
||||||
|
'designation': x.designation,
|
||||||
|
'ingredients': quirk_bold_allergens(x.ingredients),
|
||||||
|
'description': x.description,
|
||||||
|
'ab': x.ab,
|
||||||
|
'color': x.color,
|
||||||
'subs': {x.name: x.default for x in x.category.subs.all()},
|
'subs': {x.name: x.default for x in x.category.subs.all()},
|
||||||
} for x in Tikette.objects.all()]
|
} for x in Tikette.objects.all()]
|
||||||
return JsonResponse({'status': 'ok', 'tikettes': tikettes}, headers=CORS)
|
return JsonResponse({'status': 'ok', 'tikettes': tikettes}, headers=CORS)
|
||||||
@ -36,7 +52,14 @@ def generate(request):
|
|||||||
return JsonResponse({'status': 'notok', 'message': 'this isn\'t the way'})
|
return JsonResponse({'status': 'notok', 'message': 'this isn\'t the way'})
|
||||||
|
|
||||||
payload = json.loads(request.body)
|
payload = json.loads(request.body)
|
||||||
pdfpath = stickersheet.generate(payload, out_dir=settings.TIKETTE_OUT_DIR)
|
|
||||||
|
subs = dict(payload['subs'])
|
||||||
|
for key in ('designation', 'ingredients', 'description', 'color', 'AB'):
|
||||||
|
subs[key] = payload[key]
|
||||||
|
|
||||||
|
pdfpath = stickersheet.generate(template=payload['template'], subs=subs,
|
||||||
|
out_dir=settings.TIKETTE_OUT_DIR,
|
||||||
|
landscape=payload['landscape'])
|
||||||
|
|
||||||
return JsonResponse({'status': 'ok', 'file': pdfpath}, headers=CORS)
|
return JsonResponse({'status': 'ok', 'file': pdfpath}, headers=CORS)
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
const params = {
|
const params = {
|
||||||
'dluo': 'DLUO',
|
'dluo': 'DLUO',
|
||||||
'lot': 'Nº de lot',
|
'lot': 'Nº de lot',
|
||||||
'qty': 'Poids net (g)',
|
'q': 'Poids net (g)',
|
||||||
'vol': 'Volume net (cL)',
|
|
||||||
'teneur': 'Teneur en fruits (%)',
|
'teneur': 'Teneur en fruits (%)',
|
||||||
'fruit': 'Quantité de fruits pour 100g (g)',
|
'f': 'Quantité de fruits pour 100g (g)',
|
||||||
}
|
}
|
||||||
|
|
||||||
var tikats;
|
var tikats;
|
||||||
@ -26,7 +25,12 @@ function addProduct(tikette) {
|
|||||||
.toArray()
|
.toArray()
|
||||||
.reduce((obj, el) => ({...obj, [el.name]: el.value}), {});
|
.reduce((obj, el) => ({...obj, [el.name]: el.value}), {});
|
||||||
const req = {
|
const req = {
|
||||||
sticker: zett.sticker,
|
template: zett.prototempalte,
|
||||||
|
designation: zett.designation,
|
||||||
|
description: zett.description,
|
||||||
|
ingredients: zett.ingredients,
|
||||||
|
color: zett.color,
|
||||||
|
AB: zett.ab, // mind the capitalization here
|
||||||
subs,
|
subs,
|
||||||
landscape: zett.landscape,
|
landscape: zett.landscape,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user