Compare commits

..
22 Commits
Author SHA1 Message Date
niggl 405bb20601 Merge branch 'feature/36-runner_certificates' of git.odit.services:lfk/document-server into feature/36-runner_certificates 2021-03-31 16:15:51 +02:00
niggl ac572f1ea3 Added translations
ref #36
2021-03-31 16:15:49 +02:00
niggl 7fea1ca78f Added translations
ref #46
2021-03-31 16:15:42 +02:00
niggl 64fce5bd01 Now with embedded background
ref #36
2021-03-31 16:09:15 +02:00
niggl 5ba26c4cbf Removed temporary background-image fix
ref #36
2021-03-31 16:05:13 +02:00
niggl b82a32ae3e Fixed page size+background image
ref #36
2021-03-31 15:42:52 +02:00
niggl 0af9b81b38 Added new basic certificate endpoint
ref #36
2021-03-31 15:22:16 +02:00
niggl 955e11846b Added function for generateing runner certificates
ref #36
2021-03-31 15:18:36 +02:00
niggl d1577cd08d Merge branch 'feature/36-runner_certificates' of git.odit.services:lfk/document-server into feature/36-runner_certificates 2021-03-31 15:13:14 +02:00
niggl 6767c3b2d1 Added template strings
ref #36
2021-03-31 15:13:12 +02:00
niggl 2b2195727b Added template strings
ref #46
2021-03-31 15:13:08 +02:00
niggl 3ca2237953 Added backside table
ref #36
2021-03-31 15:09:14 +02:00
niggl 8d6ea4dbf9 Fixed bg image opacity overlay
ref #36
2021-03-31 15:00:12 +02:00
niggl 8b71608792 Added front certificate design
ref #36
2021-03-31 14:51:41 +02:00
niggl f1084b59a7 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-30 16:08:57 +00:00
niggl c8dc998ecd Merge pull request 'Release 0.4.3' (#40) from dev into main
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: #40
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-30 16:07:37 +00:00
niggl 3df3d26708 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-30 16:07:01 +00:00
niggl 922e762aa2 Merge branch 'dev' of git.odit.services:lfk/document-server into dev
continuous-integration/drone/push Build is passing
2021-03-30 18:06:02 +02:00
niggl c3beb3e103 🚀Bumped version to v0.4.3 2021-03-30 18:05:48 +02:00
niggl 457ea26cf8 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-30 16:05:37 +00:00
niggl c2d2b66f2f Pipeline mtu fix
continuous-integration/drone/push Build was killed
2021-03-30 18:04:54 +02:00
niggl 289a0d8671 🧾New changelog file version [CI SKIP] [skip ci] 2021-03-29 16:48:57 +00:00
10 changed files with 235 additions and 41 deletions

No files matched your search

+2
View File
@@ -113,6 +113,7 @@ steps:
tags:
- latest
registry: registry.odit.services
mtu: 1000
- name: push merge to repo
depends_on: ["clone"]
image: appleboy/drone-git-push
@@ -147,6 +148,7 @@ steps:
tags:
- '${DRONE_TAG}'
registry: registry.odit.services
mtu: 1000
trigger:
event:
- tag
+17
View File
@@ -2,8 +2,24 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v0.4.3](https://git.odit.services/lfk/document-server/compare/v0.4.3...v0.4.3)
#### [v0.4.3](https://git.odit.services/lfk/document-server/compare/v0.4.2...v0.4.3)
> 30 March 2021
- Merge pull request 'Release 0.4.3' (#40) from dev into main [`c8dc998`](https://git.odit.services/lfk/document-server/commit/c8dc998ecdccc7fc4348ecc0db552a3d7bc2eb52)
- 🧾New changelog file version [CI SKIP] [skip ci] [`289a0d8`](https://git.odit.services/lfk/document-server/commit/289a0d8671575dda911c64f79d24726d3bbee071)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3df3d26`](https://git.odit.services/lfk/document-server/commit/3df3d26708aab12590cd9c1f697cfdea8017ace4)
- 🧾New changelog file version [CI SKIP] [skip ci] [`457ea26`](https://git.odit.services/lfk/document-server/commit/457ea26cf8124009084415d12c7a0e31912a3eb1)
- 🚀Bumped version to v0.4.3 [`c3beb3e`](https://git.odit.services/lfk/document-server/commit/c3beb3e1032492f9a8304c4b099a700ad0d1d2cf)
- Pipeline mtu fix [`c2d2b66`](https://git.odit.services/lfk/document-server/commit/c2d2b66f2f6fbd30c2027fd3dab393db5219eb44)
#### [v0.4.2](https://git.odit.services/lfk/document-server/compare/v0.4.0...v0.4.2)
> 29 March 2021
- Merge pull request 'Release 0.4.2' (#39) from dev into main [`ce3053c`](https://git.odit.services/lfk/document-server/commit/ce3053c0ba5404ebd8d4cbd5386c454d8cd8c4d2)
- 🚀Bumped version to v0.4.1 [`d17108f`](https://git.odit.services/lfk/document-server/commit/d17108f4b9f041727afcee5c171d5d8f9a3c677c)
- 🧾New changelog file version [CI SKIP] [skip ci] [`0ade575`](https://git.odit.services/lfk/document-server/commit/0ade57536e372a62356fe2f329f6cfe04bcfc397)
- Fixed faulty object mapping [`9584bfe`](https://git.odit.services/lfk/document-server/commit/9584bfed8ba5b8c856b55aa90969874d4d6751ff)
@@ -11,6 +27,7 @@ All notable changes to this project will be documented in this file. Dates are d
- 🧾New changelog file version [CI SKIP] [skip ci] [`b8f0d1f`](https://git.odit.services/lfk/document-server/commit/b8f0d1fa60da4e3262623cecf4a78c8a9a322f8c)
- 🧾New changelog file version [CI SKIP] [skip ci] [`073433f`](https://git.odit.services/lfk/document-server/commit/073433f308f2f8e7ada97dd0b3896fb541e85711)
- 🧾New changelog file version [CI SKIP] [skip ci] [`a478081`](https://git.odit.services/lfk/document-server/commit/a47808172797e12800c46d17ccf4b458f4a506de)
- 🧾New changelog file version [CI SKIP] [skip ci] [`6608456`](https://git.odit.services/lfk/document-server/commit/6608456c686e51d3aa16e630ba72c7c29e428b2a)
- 🧾New changelog file version [CI SKIP] [skip ci] [`0839ff6`](https://git.odit.services/lfk/document-server/commit/0839ff63593cfb9e8339b2b2a86dbe7e2afb5dcb)
- Now using our own images to build stuff 🚀 [`96886c7`](https://git.odit.services/lfk/document-server/commit/96886c74bc397e1ee793e83e0d01a6bbaa953da3)
- 🚀Bumped version to v0.4.2 [`1cbe5a1`](https://git.odit.services/lfk/document-server/commit/1cbe5a1614c40fbf23f032be60039ddb1472ca94)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@odit/lfk-document-server",
"version": "0.4.2",
"version": "0.4.3",
"description": "The document generation server for the LfK! runner system. This generates certificates, sponsoring aggreements and more",
"main": "src/app.ts",
"scripts": {
+29 -3
View File
@@ -10,6 +10,7 @@ import { PDFDocument } from 'pdf-lib';
import puppeteer from "puppeteer";
import { awaitAsyncHandlebarHelpers, helpers } from './asyncHelpers';
import { config } from './config';
import { CertificateRunner } from './models/CertificateRunner';
import { Runner } from './models/Runner';
import { RunnerCard } from './models/RunnerCard';
import { RunnerGroup } from './models/RunnerGroup';
@@ -153,13 +154,37 @@ export class PdfCreator {
await i18next.changeLanguage(locale);
const template_source = fs.readFileSync(`${this.templateDir}/runner_card.html`, 'utf8');
const template = Handlebars.compile(template_source);
let result = template({ cards, cards_swapped, eventname: "LfK! 2069", codeformat: "qrcode" })
let result = template({ cards, cards_swapped, eventname: config.eventname, codeformat: codeformat })
result = await awaitAsyncHandlebarHelpers(result);
fs.writeFileSync("lelelelele.tmp", result);
const pdf = await this.renderPdf(result, { format: "A4", landscape: false });
return pdf
}
/**
* Generate sponsoring contract pdfs.
* @param runner The runner you want to generate the contracts for.
* @param locale The locale used for the contracts (default:en)
*/
public async generateRunnerCertficates(runners: CertificateRunner[], locale: string = "en"): Promise<Buffer> {
if (runners.length > 50) {
let pdf_promises = new Array<Promise<Buffer>>();
let i, j;
for (i = 0, j = runners.length; i < j; i += 50) {
let chunk = runners.slice(i, i + 50);
pdf_promises.push(this.generateRunnerCertficates(chunk, locale));
}
const pdfs = await Promise.all(pdf_promises);
return await this.mergePdfs(pdfs);
}
await i18next.changeLanguage(locale);
const template_source = fs.readFileSync(`${this.templateDir}/runner_certificate.html`, 'utf8');
const template = Handlebars.compile(template_source);
let result = template({ runners, eventname: config.eventname });
result = await awaitAsyncHandlebarHelpers(result);
const pdf = await this.renderPdf(result, { format: "A4", landscape: false, printBackground: true });
return pdf;
}
/**
* Converts all images in html to base64.
* Works with image files in the template directory or images from urls.
@@ -167,6 +192,7 @@ export class PdfCreator {
*/
public async imgToBase64(html): Promise<string> {
const $ = cheerio.load(html)
$('img').each(async (index, element) => {
let imgsrc = $(element).attr("src");
if (imgsrc.startsWith("data:image")) {
@@ -192,7 +218,7 @@ export class PdfCreator {
image = `data:${img_type};base64,${image}`
$(element).attr("src", image)
})
});
return $.html();
}
+34
View File
@@ -1,5 +1,6 @@
import { Authorized, Body, JsonController, Post, QueryParam, Res } from 'routing-controllers';
import { OpenAPI } from 'routing-controllers-openapi';
import { CertificateRunner } from '../models/CertificateRunner';
import { Runner } from '../models/Runner';
import { RunnerCard } from '../models/RunnerCard';
import { PdfCreator } from '../PdfCreator';
@@ -54,6 +55,25 @@ export class PdfController {
return contracts;
}
@Post('/certificates')
@OpenAPI({ description: "Generate runner certificate pdfs from certificate runner objects.<br>You can choose your prefered locale by passing the 'locale' query-param.<br> If you provide more than 100 runenrs this could take a moment or two (we tested up to 1000 runners in about 70sec so far)." })
async generateCertificates(@Body({ validate: true, options: { limit: "500mb" } }) runners: CertificateRunner[], @Res() res: any, @QueryParam("locale") locale: string, @QueryParam("download") download: boolean) {
if (!this.initialized) {
await this.pdf.init();
this.initialized = true;
}
if (!Array.isArray(runners)) {
runners = [runners];
}
runners = this.mapCertificatRunnersGroupNames(runners)
const certificates = await this.pdf.generateRunnerCertficates(runners, locale);
res.setHeader('content-type', 'application/pdf');
if (download) {
res.setHeader('Content-Disposition', 'attachment; filename="certificates.pdf"')
}
return certificates;
}
private mapRunnerGroupNames(runners: Runner[]): Runner[] {
let response = new Array<Runner>();
for (let runner of runners) {
@@ -68,6 +88,20 @@ export class PdfController {
return response;
}
private mapCertificatRunnersGroupNames(runners: CertificateRunner[]): CertificateRunner[] {
let response = new Array<CertificateRunner>();
for (let runner of runners) {
if (!runner.group.parentGroup) {
runner.group.fullName = runner.group.name;
}
else {
runner.group.fullName = `${runner.group.parentGroup.name}/${runner.group.name}`;
}
response.push(runner)
}
return response;
}
private mapCardGroupNames(cards: RunnerCard[]): RunnerCard[] {
let response = new Array<RunnerCard>();
for (let card of cards) {
+27 -19
View File
@@ -1,20 +1,28 @@
{
"address": "Adresse",
"city": "Stadt",
"date": "Datum",
"firstname": "Vorname",
"group": "Team/Klasse",
"house_number": "Hausnummer",
"id": "ID",
"lastname": "Nachname",
"location": "Ort",
"please_use_blockletters": "Bitte in DRUCKBUCHSTABEN schreiben",
"postalcode": "Postleitzahl",
"signature": "Unterschrift",
"sponsor": "Sponsor",
"sponsoring_address_condition": "Muss ausgefüllt werden, wenn Sie eine Spendenquittung benötigen - Spendenquittungen können erst ab einem Gesamtbetrag von {{sponsoring_receipt_minimum_amount}}{{currency_symbol}} ausgestellt werden",
"sponsoring_amount_per_distance": "mit einem Betrag von _____{{currency_symbol}} pro gelaufenem Kilometer zu unterstützen.",
"sponsoring_subtitle": "Ich/Wir sind bereit anlässlich des {{eventname}}",
"sponsoring_title": "Sponsoringerklärung",
"street": "Straße"
}
"address": "Adresse",
"city": "Stadt",
"date": "Datum",
"firstname": "Vorname",
"group": "Team/Klasse",
"house_number": "Hausnummer",
"id": "ID",
"lastname": "Nachname",
"location": "Ort",
"please_use_blockletters": "Bitte in DRUCKBUCHSTABEN schreiben",
"postalcode": "Postleitzahl",
"signature": "Unterschrift",
"sponsor": "Sponsor",
"sponsoring_address_condition": "Muss ausgefüllt werden, wenn Sie eine Spendenquittung benötigen - Spendenquittungen können erst ab einem Gesamtbetrag von {{sponsoring_receipt_minimum_amount}}{{currency_symbol}} ausgestellt werden",
"sponsoring_amount_per_distance": "mit einem Betrag von _____{{currency_symbol}} pro gelaufenem Kilometer zu unterstützen.",
"sponsoring_subtitle": "Ich/Wir sind bereit anlässlich des {{eventname}}",
"sponsoring_title": "Sponsoringerklärung",
"street": "Straße",
"urkunde": "Urkunde",
"hat-beim-eventname": "Hat beim {{../eventname}}",
"fuer-den-guten-zweck-zurueckgelegt": "für den guten Zweck zurückgelegt",
"sponsorings": "Sponsorings",
"sponsor-in": "Sponsor:in",
"betrag-km": "Betrag/KM",
"gesamtbetrag": "Gesamtbetrag",
"gesamt": "Gesamt"
}
+27 -18
View File
@@ -1,19 +1,28 @@
{
"address": "Address",
"city": "City",
"date": "date",
"firstname": "First name",
"group": "Team/class",
"house_number": "House number",
"lastname": "Last name",
"location": "Location",
"please_use_blockletters": "Please write in BLOCK LETTERS.",
"postalcode": "Postal code",
"signature": "Signature",
"sponsor": "sponsor",
"sponsoring_address_condition": "You have to provide an address if you want a donation receipt - Donation receipts can't be issued for total donation amounts under {{sponsoring_receipt_minimum_amount}}{{currency_symbol}}",
"sponsoring_amount_per_distance": "with the amount of _____{{currency_symbol}} per kilometer run.",
"sponsoring_subtitle": "On the ocation of the {{eventname}} I/We want to support",
"sponsoring_title": "Sponsoring contract",
"street": "Street"
}
"address": "Address",
"city": "City",
"date": "date",
"firstname": "First name",
"group": "Team/class",
"house_number": "House number",
"lastname": "Last name",
"location": "Location",
"please_use_blockletters": "Please write in BLOCK LETTERS.",
"postalcode": "Postal code",
"signature": "Signature",
"sponsor": "sponsor",
"sponsoring_address_condition": "You have to provide an address if you want a donation receipt - Donation receipts can't be issued for total donation amounts under {{sponsoring_receipt_minimum_amount}}{{currency_symbol}}",
"sponsoring_amount_per_distance": "with the amount of _____{{currency_symbol}} per kilometer run.",
"sponsoring_subtitle": "On the ocation of the {{eventname}} I/We want to support",
"sponsoring_title": "Sponsoring contract",
"street": "Street",
"urkunde": "Certifcate",
"fuer-den-guten-zweck-zurueckgelegt": "for our good cuse at the {eventname}",
"hat-beim-eventname": "Ran",
"id": "ID",
"sponsorings": "Donations",
"sponsor-in": "Donor",
"betrag-km": "Amount/KM",
"gesamtbetrag": "Total",
"gesamt": "Combined"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+98
View File
@@ -0,0 +1,98 @@
<html>
<head>
<meta charset="utf8">
<title>Sponsoring contract</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
padding: 1.2cm 2cm 1.2cm 2cm;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqsAAASrCAYAAAC1y/HLAAAgAElEQVR4nOzdvXLbWrY26nfv03nLSdeXNZygvgC1W76CBeMGWs5RZSo8kaUUAS0hQCr5CkRXIZfWDUBYV2B2FyImQqc7MTs7WZ8AkxZN82cCmH8A3qdq1VrLAoFpiSIHB8YcAyAiIiIiIiIiIiIiIiIiIiIiIiIiItLrv2wvgIiISKeiKM4AnAPY/BsAfts6ZPvP9ym3/nsJ4N8AavHPMoqitaKlEtEeDFaJiGhUiqLwAHwG4AEIDVxyjSaIXQK4ZfBKpBaDVSIiGp2iKL6jyZiadhlF0cLCdYlG679tL4CIiEiD0tJ1/27pukSjxWCViIicl2XZWZZlV1mWyWZLf9e6oMPCNgfHcWwj+0s0KH+yvQAiIqJDsiwLAXwEMBN/tAawkHjoE4AHLYs67qwoiosoip4kj/8ex/ETmuD6Kc9z1rsS7WBmlYiInJNl2SzLsmcAz3gNVAHJ2+xik1OpfmVS2pQCPAG4QBNYf4/j+DGO4ws9yyIaJm6wIiIiZ2RZNsPrTv5D3iRJcjIDWRTFFYA7NStrpY6i6K3MgXEcH1rjJoP8Jc/zWt3SiIaHmVUiIrIuy7JQZFIfcDxQBeTrQmVvxavmFUVxrG/rtkNrPANwBeAljuNnZltpyphZJSIia8SGqTv8fKv/lEWSJJcyBxZF8Q3HG/7rch9F0bXMgXEcv+B0gA40QwhuwdpWmhhmVomIyIosy64AvKBdoAo0NZ6yypbnVqXNGmUzwB6azPNLHMc37CRAU8HMKhERGSWyqY/oN13qQ5IkJ4M8cTv+W4/r9PE2iqL61EHiFv9jh/OvAXwBcM9MK40ZM6tERGRMlmUXaLKpYc9T/SZzUBRFSzS3z22Qyq7med61tvYMzWY0Zlpp1BisEhGREVmW3aHJIKoIqnTcZlftY4tj+6xxO2id9TgPkZMYrBIRkVZi+tQzmt3tqnhZlslunPpD4XXbOC+KwpM8VsUazwA8xHH8wu4BNCYMVomISJssyzw0jf1DDaeXylyKaVK2ajpDyeNUZn89AI+i5ZWNTghESjFYJSIiLUTmU2frqCGUAkhNsxKN/2vF1w4BfIvj+I71rDRkDFaJiEg5Eag+Q0196iGeyNzKsFUKcFEUhez3QFdAvRkuwNIAGiQGq0REpJShQHVDNgCzlVkF5EsBdAbUZ3gtDfA0XodIOQarRESkjOFAFZCvW13D8VIAmBlgEKIpDbgxcC0iJRisEhGREqLZv8lAFQDOh1AKIHOQaOxf6l0KANHqillWGgoGq0RE1JulQHUjlDzOVmb1rCgK2XKF37Wu5Gchmiwra1nJaQxWiYhIhQfo2/V/itRtdjH6dKl3KQdJTdyCmczqtk0t653h6xJJY7BKRES9ZFl2g3ZtpFS7EJldGSYzl9tkSwFsjYe9iuP4kS2uyEUMVomIqLMsy0I0oz5tc70rgFcUhWzmudS5kCMuADwzYCXXMFglIqI+Hm0vQJAtBbCVuQTkA2pb2V+gKeVw5WdKBIDBKhERjUPY4li2sDouZA0ruYTBKhER9VHaXoBwlmWZbObSVgur86IovFMHGWxhdcwVuwSQKxisEhFRHzZvWe+SLQV4ArDWvJZDhlAKsPHAPqzkAgarRETUR2l7AVvaZAJZCnDaGVi/Sg5gsEpERJ0lSVLDXu/SXWdi3KsMW6UAYVEUJ3fbW2xhteuco1nJNgarRETUlwu3rDc+Sh5nK7MKyGeAS52LaOFzHMe2Bj4QMVglIqLebAZ+u6QCwSiK1nC/FMClDwEPthdA08VglYiIekmSZAl7G5Z2eQMoBRhaZhVoygGubC+CponBKhERqTC47CosrrkoipNrdKSF1bbPnG5FNjBYJSIiFWxlKfeRbWFVw97msCGWApwB4LAAMo7BKhERqeBSZvU8yzJP8lhbweAQSwEAYMbNVmQag1UiIuotSZI13GlhBbhfCnBWFMXJoM+hFlbbmF0loxisEhHRUS0yaS7dspYtBbAZDMq22Sp1LqKDMI7j0PYiaDoYrBIR0UFi3OZnycNdKgUIsyyT3Qxka91DGr26S/Y5QdQbg1UiIjrmAkAoc6BoYVXrXExLssGgrc1hnkwpANzLrALMrpJBDFaJiOiYjwDOWgQmpb6ltCZbCvAEe31iw1MHiBZWLtUDb3yyvQCaBgarRES0lygB2GT+QsmHuXTL+mIApQCydasufV83LsRzhEgrBqtERHTI9m102b6gpYZ19BFKHmerFOC8KApP4jiX6oG3MbtK2jFYJSKiQ37b+u9zmelFooVVqW1F7ckG2TaDwfDUAaKFlSsjbbfNbC+Axo/BKhERHbK7QSmUfJxLt6ylNllFUbSGvYB1CAH1IWdxHM9sL4LGjcEqERH94sCGqt/2/Nk+pbqV9HaWZZnrXQEuiqKQqa11aaTtNtlgm6gTBqtERLRPuOfPpII+B1tYyQbZNjOXMt9bFzOrADdakWYMVomIaJ99AZ7XIigp1S2lN9lSgBr2WkSdDKgdbmEFyPe0JWqNwSoREe0TtvzzXS7VrXpZlrk+MnbI06wAlgKQRgxWiYjoJ3EcHwvspG6pJ0ni2i1r2X6mttZ9VhTFkEsBQpluEURdMFglIqJdx4LVNrd7XQqsZEsBbNbbnsxOOtzCCmApAGnCYJWIiPY5FBCdnci8bnNp97qXZZkneaytIDuUPM6lDwHbZDeyEbXCYJWIiH6S5/kCwFsAl9i/oSeUPJVrQZXrLay8oihkPgi49CFgW2h7ATRODFaJiOgXeZ6v8zxf5Hn+DsA7AAu8ZlulNtMkSVLDrRZWUnWrURQ9wd6tdpk1uvYhYKNNtwgiaQxWiYjoqDzPl3meX+I129pmI41LgdX5GEoBHG9hJVsiQiSNwSoREUnZybbKcu2WdSh5nK0WUedFUXgSx7nawop1q6Qcg1UiItLGwRZWsv1AS52LOGHILayYWSXlGKwSEZFuLgVWF1mWnSxjiKJoDXvrPlm36nALKwarpByDVSIi0s21UgDXp0WdF0UhUxfs0oeAjTMOByDVGKwSEZFuT3CrK8BYSgFc+xCwwewqKcVglYiItEqSpE6SZNNJwIVb16HMQVEU1bC3614moHYxswoAnu0F0LgwWCUiIiOSJFmgaX91C7tB61mWZbKlAF+1ruSwk+tzuIWVZ3sBNC4MVomIyJgkSdZJktzgddCALc6XAhRFIRNQu9jC6s+2F0DjwmCViIh+CILAC4Ig1H0dURpwiSZoLXVfbw+pzGoURUvYq7eVCahL3YvogDWrpBSDVSIi2uYBeA6C4NlQ0LpMkuQ9gPcwe0v7LMsy2aDKVm2oTClACTfqgIm0YbBKRET7hDAbtJZJkrxDswmr1n094WQ/U8HWrfazoihCieNKzesgsorBKhERHRPCbNC6QFMaYGITlmwpQAl72UuZUgDX6lbZZ5WUYrBKREQyQhgKWrc2Yb2F3k1Y3hhKAeBeZpU1q6QUg1UiImojhNmg9RJN0Fpquozr06y8oiiOBn95ntdws4UVkRIMVomIaFsteVwIc0FrrXETlvMtrDDM7CqRMgxWiYjoh6qq6pYPCWEuaNWxCes8yzLv1EFRFK1hrxRgiHWrRMowWCUiIhVCmAtaF2J8q6pNWK6XApwXReEdO8CxFla17QXQuDBYJSKiXXWPx4YwF7TeoKlnve95KpYCqFXbXgCNC4NVIiLaVSs4RwgDQavYhHWNJmjteps+zLLsZLulKIpq2NvI9JvEMSwFoFFisEpERDqFMBO01kmSfECzCavscArZUoCvHc6twkVRFKcC6tLEQohMY7BKRES7/tBwzhBmgtZSdA5ouwlr8KUADrWwqm0vgMaFwSoREe3SuVEnhJmgte0mrAvJUoAl7AVjMgF1qXsREv5lewE0LgxWiYhol4nsXAgzQesNmnrWW8k1ybDVwiqUOMaFulVXuhLQSDBYJSKiXSZvJYfQHLS2GN8qWwpgKyA8K4riVClACfvBogulCDQiDFaJiOgnVVWtYf5Wdwj9QWstxrce2oQltckqiqIS9gLCIZQCMFglpRisEhHRPrYCjhD6g9bNJqwP+DkoP8uyTLYrgK1SAJn12SwFqPM8t53ZpZFhsEpERPvo6AjQRgj9QeuT2IR1jddMqUw/U8BuKcD5iWNKEwtx8No0UgxWiYhoH1du5YbQH7Te43UTlmxmtdSxFkkfj33Rcgsr2x9yaIQYrBIR0S+qqiptr2FHCI1B69YmrPcyx0dRtIbbpQCl7kUcYOt7QiPGYJWIiA4pbS9gjxB6g9a6xeG2SgG8oii8E8fYWNuS9aqkA4NVIiI6xOVbuiEM9Gk9obR0XeD0NKvS0Dq22RpFSyPHYJWIiA4pbS9AQghLQWsURTXs1YYerVsVTN+SZwkAacFglYiI9nKwbvWYEHaCVlvZxHOJUgCTmfGl2NhFpByDVSIiOqa0vYCWQpgNWkvYGxAQnvi6yUwnSwBIGwarRER0jMt1q8eEMBC0RlG0RNP26hLmp34dnWYlMp21kZWwBIA0+i/bCyAiIncFQXAO4JvtdShQArjVXdpQFMUMwGcAns7rbHkj2mjtFcfxHYArzWuo8zx/q/kaNGHMrBIR0UFVVS1h7za3SiHMZFoXURS9RTPKtdR1nS3hia+byIzXBq5BE8ZglYiIThnTLd4QZoLWpyiK3qMZMlDqug5OlwKY+NmdGbgGTRiDVSIiOmWodavHhDATtJYiaH0HYKHhEjLTrHQHrOdxHDNgJW0YrBIR0SljyqzuCmFoI1YURZdoNmMtFJ76rCiKUwGriQ8buutiacIYrBIR0VFVVa1hr/m9KSHMBK31VtB6CzX1wL+d+LqJDxuf4jg+N3AdmiAGq0REJGMqfTRDmAtab6AmaD01erWG/k1QZwAeWA5AOjBYJSIiGWMuLine truncated
background-repeat: no-repeat;
background-size: 11cm;
background-position: 5cm 5cm;
background-color: rgba(255, 255, 255, 0.7);
background-blend-mode: lighten;
/* border-style: solid; */
}
body.A4 .sheet {
width: 210mm;
height: 296mm
}
.certificate-footer {
position: absolute;
bottom: 0cm;
}
td {
border: 1px solid black;
font-size: large;
font-weight: 600;
}
</style>
</head>
<body class="A4 landscape">
{{#each runners}}
<article class="sheet">
<header class="content has-text-centered">
<h1 style="font-size: 3cm; font-weight: bold;">{{__ "urkunde"}}</h1>
</header>
<main class="content has-text-centered" style="padding-top: 3cm;">
<p style="width: 50%; font-size: 4vw; font-weight: bold; margin-bottom: 0; display: inline;">{{this.firstname}}
{{this.middlename}} {{this.lastname}}
</p>
<p style="font-size: 1cm; margin-bottom: 0;">{{__ "hat-beim-eventname"}}</p>
<p style="font-size: 2cm; font-weight: bold; margin-bottom: 0;">{{this.distance}}km</p>
<p style="font-size: 1cm;">{{__ "fuer-den-guten-zweck-zurueckgelegt"}}</p>
</main>
<footer class="certificate-footer">
<img src="./certificate_footer.png">
</footer>
</article>
<article class="sheet">
<header class="content has-text-centered">
<h1 style="font-size: 2.5cm;">{{__ "sponsorings"}}</h1>
</header>
<main>
<table style="border: solid; width: 17cm;">
<thead>
<td class=".td-head">{{__ "sponsor-in"}}</td>
<td>{{__ "betrag-km"}}</td>
<td>{{__ "gesamtbetrag"}}</td>
</thead>
<tbody>
{{#each this.distanceDonations}}
<tr>
<td>{{this.donor.firstname}} {{this.donor.middlename}} {{this.donor.lastname}}</td>
<td>{{this.amountPerDistance}}</td>
<td>{{this.amount}}]</td>
</tr>
{{/each}}
</tbody>
<tfoot>
<td>{{__ "gesamt"}}</td>
<td>0.5€</td>
<td>1€</td>
</tfoot>
</table>
</main>
<footer class="certificate-footer">
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
</footer>
</article>
{{/each}}
</body>
</html>