import { AufnahmeClient, BenutzerClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js"; import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016 } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016.js"; import { getEmpfehlungen } from "#lib/XML/getEmpfehlungen.js"; import { Enums } from "@ibcornelsen/database/server"; import * as fs from "fs" import moment from "moment"; import { PDFDocument, PDFFont, PDFImage, PDFName, PDFNumber, PDFPage, rgb, RotationTypes, StandardFonts, TextAlignment } from "pdf-lib"; import { addCheckMark } from "./utils/checkbox.js"; import { addText } from "./utils/text.js"; import { addAnsichtsausweisLabel, addDatumGEG } from "./utils/helpers.js"; export async function pdfVerbrauchsausweisGewerbe(ausweis: VerbrauchsausweisGewerbeClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: UploadedGebaeudeBild[], user: BenutzerClient) { const VerbrauchsausweisWohnenGEG2024PDF = fs.readFileSync(new URL("./templates/GEG24_Nichtwohngebaeude.pdf", import.meta.url), "base64"); const pdf = await PDFDocument.load(VerbrauchsausweisWohnenGEG2024PDF) const pages = pdf.getPages() // const template = VerbrauchsausweisWohnen2016Template as Template; const berechnungen = await endEnergieVerbrauchVerbrauchsausweisGewerbe_2016(ausweis, aufnahme, objekt); const empfehlungen = getEmpfehlungen(ausweis, aufnahme, objekt) const height = pages[0].getHeight() const font = await pdf.embedFont(StandardFonts.Helvetica) const bold = await pdf.embedFont(StandardFonts.HelveticaBold) const form = pdf.getForm() form.updateFieldAppearances(font) const fillFormField = (name: string, value: string, fontSize: number = 8, alignment: TextAlignment = TextAlignment.Left) => { const field = form.getTextField(name) field.setFontSize(fontSize) field.setText(value) field.setAlignment(alignment) } const toggleCheck = (name: string, checked: boolean = true) => { const field = form.getCheckBox(name) if (checked) { field.check() } else { field.uncheck() } } pages[0].drawText(aufnahme.gebaeudetyp || "", { x: 211, y: height - 166, size: 10 }) pages[0].drawText(objekt.adresse || "", { x: 211, y: height - 194, size: 10 }) pages[0].drawText(aufnahme.gebaeudeteil || "", { x: 211, y: height - 214.5, size: 10 }) pages[0].drawText(aufnahme.baujahr_gebaeude?.toString() || "", { x: 211, y: height - 229.5, size: 10 }) pages[0].drawText(aufnahme.baujahr_heizung?.toString() || "", { x: 211, y: height - 250, size: 10 }) pages[0].drawText(aufnahme.nutzflaeche?.toString() || "", { x: 211, y: height - 271.5, size: 10 }) pages[0].drawText(`${aufnahme.brennstoff_1}, ${aufnahme.brennstoff_2 || ""}`, { x: 211, y: height - 285, size: 10 }) const erneuerbareEnergienVerwendung = [] if (ausweis.alternative_heizung) { erneuerbareEnergienVerwendung.push("Heizung") } if (ausweis.alternative_kuehlung) { erneuerbareEnergienVerwendung.push("Kühlung") } if (ausweis.alternative_lueftung) { erneuerbareEnergienVerwendung.push("Lüftung") } if (ausweis.alternative_warmwasser) { erneuerbareEnergienVerwendung.push("Warmwasser") } pages[0].drawText(erneuerbareEnergienVerwendung.join(", "), { x: 358, y: height - 325, size: 8 }) if (ausweis.warmwasser_enthalten) { pages[0].drawText(`${aufnahme.brennstoff_1}, ${aufnahme.brennstoff_2 || ""}`, { x: 211, y: height - 299, size: 10 }) } if (aufnahme.lueftung === Enums.Lueftungskonzept.Fensterlueftung) { addCheckMark(pages[0], 213, height - 334) } else if (aufnahme.lueftung === Enums.Lueftungskonzept.Schachtlueftung) { addCheckMark(pages[0], 213, height - 345) } else if (aufnahme.lueftung === Enums.Lueftungskonzept.LueftungsanlageMitWaermerueckgewinnung) { addCheckMark(pages[0], 355, height - 334) } else if (aufnahme.lueftung === Enums.Lueftungskonzept.LueftungsanlageOhneWaermerueckgewinnung) { addCheckMark(pages[0], 355, height - 345) } // Kühlung if (ausweis.wird_gekuehlt) { addCheckMark(pages[0], 213, height - 362.5) } else { addCheckMark(pages[0], 355, height - 373.5) } if (ausweis.ausstellgrund === Enums.Ausstellgrund.Neubau) { addCheckMark(pages[0], 213, height - 406) } else if (ausweis.ausstellgrund === Enums.Ausstellgrund.Vermietung) { addCheckMark(pages[0], 213, height - 417) } else if (ausweis.ausstellgrund === Enums.Ausstellgrund.Modernisierung) { addCheckMark(pages[0], 344.5, height - 406) } else if (ausweis.ausstellgrund === Enums.Ausstellgrund.Sonstiges) { addCheckMark(pages[0], 463, height - 417) } // Aushangpflicht // addCheckMark(pages[0], 463, height - 406) const gebaeudeBild = bilder.find(image => image.kategorie === Enums.BilderKategorie.Gebaeude); if (gebaeudeBild) { let image: PDFImage; try { image = await pdf.embedJpg(gebaeudeBild?.base64) } catch(e) { image = await pdf.embedPng(gebaeudeBild?.base64) } pages[0].drawImage(image, { x: 460.5, y: height - 289, width: 111, height: 138 }) } // Checkmark Angabe energetische Qualität des Gebäudes. addCheckMark(pages[0], 40, height - 550) // Datenerhebung durch Eigentümer addCheckMark(pages[0], 295, height - 580) // Ausstellungsdatum pages[0].drawText(moment().format("DD.MM.YYYY"), { font, size: 10, x: 508, y: height - 752 }) // Gültig bis pages[0].drawText(moment().add(10, "years").format("DD.MM.YYYY"), { font: bold, size: 10, x: 90, y: height - 113 }) // Stempel und Unterschrift if (ausweis.ausgestellt) { const stempel = await pdf.embedPng(fs.readFileSync(new URL("./images/stempel-unterschrift.png", import.meta.url), "base64")); const stempelHeight = 60 pages[0].drawImage(stempel, { x: 450, y: height - 750, height: stempelHeight, width: stempel.width / (stempel.height / stempelHeight) }) } // Aussteller const aussteller = await pdf.embedPng(fs.readFileSync(new URL("./images/aussteller.png", import.meta.url), "base64")); pages[0].drawImage(aussteller, { x: 40, y: height - 750, width: 100, height: 50 }) // /* -------------------------------- Seite 2 -------------------------------- */ // const co2Emissionen = fillFormField("co2emissionen", berechnungen?.co2EmissionenGesamt.toString(), 8, TextAlignment.Center) const addEnergieverbrauchSkalaPfeile = async (page: PDFPage) => { const pfeilNachUnten = await pdf.embedPng(fs.readFileSync(new URL("../../../public/images/pfeil-nach-unten.png", import.meta.url), "base64")) // Wir müssen den berechneten Wert zwischen 0 und 1000 als Wert zwischen 0 und 1 festlegen const endenergieverbrauchTranslationPercentage = Math.min(1000, Math.max(0, berechnungen?.endEnergieVerbrauchGesamt || 0)) / 1000 const stromVerbrauchTranslationPercentage = Math.min(1000, Math.max(0, berechnungen?.endEnergieVerbrauchStrom || 0)) / 1000 const minTranslation = 78 const maxTranslation = 512 const endenergieverbrauchTranslationX = minTranslation + (maxTranslation - minTranslation) * endenergieverbrauchTranslationPercentage; const stromVerbrauchTranslationX = minTranslation + (maxTranslation - minTranslation) * stromVerbrauchTranslationPercentage; const pfeilWidth = 20 const margin = 5; page.drawImage(pfeilNachUnten, { x: endenergieverbrauchTranslationX, y: height - 210, width: pfeilWidth, height: 30 }) const endEnergieVerbrauchGesamtText = `${berechnungen?.endEnergieVerbrauchGesamt.toString()}kWh/(m²a)`; const stromVerbrauchGesamtText = `${berechnungen?.endEnergieVerbrauchStrom.toString()}kWh/(m²a)`; if (endenergieverbrauchTranslationPercentage > 0.5) { page.drawText("Endenergieverbrauch Wärme", { x: endenergieverbrauchTranslationX - margin - font.widthOfTextAtSize("Endenergieverbrauch", 10), y: height - 191, size: 10 }) page.drawText(endEnergieVerbrauchGesamtText, { x: endenergieverbrauchTranslationX - margin - bold.widthOfTextAtSize(endEnergieVerbrauchGesamtText, 10), y: height - 205, size: 10, font: bold }) } else { page.drawText("Endenergieverbrauch Wärme", { x: endenergieverbrauchTranslationX + pfeilWidth + margin, y: height - 191, size: 10 }) page.drawText(endEnergieVerbrauchGesamtText, { x: endenergieverbrauchTranslationX + pfeilWidth + margin, y: height - 205, size: 10, font: bold }) } page.drawImage(pfeilNachUnten, { x: stromVerbrauchTranslationX, y: height - 354, width: pfeilWidth, height: 30 }) if (endenergieverbrauchTranslationPercentage > 0.5) { page.drawText("Endenergieverbrauch Strom", { x: stromVerbrauchTranslationX - margin - font.widthOfTextAtSize("Primärenergieverbrauch", 10), y: height - 335, size: 10 }) page.drawText(stromVerbrauchGesamtText, { x: stromVerbrauchTranslationX - margin - bold.widthOfTextAtSize(stromVerbrauchGesamtText, 10), y: height - 349, size: 10, font: bold }) } else { page.drawText("Endenergieverbrauch Strom", { x: stromVerbrauchTranslationX + pfeilWidth + margin, y: height - 335, size: 10 }) page.drawText(stromVerbrauchGesamtText, { x: stromVerbrauchTranslationX + pfeilWidth + margin, y: height - 349, size: 10, font: bold }) } } addEnergieverbrauchSkalaPfeile(pages[2]) if (ausweis.warmwasser_enthalten) { addCheckMark(pages[2], 41, height - 269) } if (ausweis.kuehlung_enthalten) { addCheckMark(pages[2], 41, height - 281) } if (ausweis.stromverbrauch_enthaelt_heizung) { addCheckMark(pages[2], 41, height - 456) } if (ausweis.stromverbrauch_enthaelt_warmwasser) { addCheckMark(pages[2], 131, height - 456) } if (ausweis.stromverbrauch_enthaelt_lueftung) { addCheckMark(pages[2], 218, height - 456) } if (ausweis.stromverbrauch_enthaelt_beleuchtung) { addCheckMark(pages[2], 281, height - 456) } if (ausweis.stromverbrauch_enthaelt_kuehlung) { addCheckMark(pages[2], 422, height - 456) } if (ausweis.stromverbrauch_enthaelt_sonstige) { addCheckMark(pages[2], 492, height - 456) } addText(pages[2], berechnungen?.primaerEnergieVerbrauchGesamt.toString() || "", 475, height - 614, 10, font) addText(pages[2], berechnungen?.co2EmissionenGesamt.toString() || "", 475, height - 633, 10, font) // const primaerenergiebedarfIst = fillFormField("primaerenergiebedarf_ist", berechnungen?.primaerEnergieVerbrauchGesamt.toString()) /* -------------------------------- Seite 3 -------------------------------- */ // Verbräuche const addVerbrauchGenerator = () => { let i = 0; let yOffset = 14.6; const initialHeight = 297 const initialXOffset = 36; return (zeitraum_von?: string, zeitraum_bis?: string, energietraeger?: string, primaerfaktor?: string, energieverbrauch?: string, anteil_warmwasser?: string, anteil_kaelte?: number, anteil_heizung?: string, klimafaktor?: string, strom?: number) => { pages[2].drawText(zeitraum_von || "", { x: initialXOffset, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(zeitraum_bis || "", { x: initialXOffset + 47, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(energietraeger || "", { x: initialXOffset + 94, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(primaerfaktor || "", { x: initialXOffset + 232, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(energieverbrauch || "", { x: initialXOffset + 275, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(anteil_warmwasser || "", { x: initialXOffset + 325, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(anteil_kaelte?.toString() || "", { x: initialXOffset + 378, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(anteil_heizung || "", { x: initialXOffset + 430, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(klimafaktor || "", { x: initialXOffset + 464, y: initialHeight - (i * yOffset), size: 8, font }) pages[2].drawText(strom?.toString() || "", { x: initialXOffset + 504, y: initialHeight - (i * yOffset), size: 8, font }) i++; } } const addVerbrauch = addVerbrauchGenerator(); if (!ausweis.warmwasser_enthalten) { // Mit Warmwasserzuschlag addVerbrauch( moment(ausweis.startdatum).format("MM.YYYY"), moment(ausweis.startdatum).add(3, "years").format("MM.YYYY"), aufnahme.brennstoff_1 || "", berechnungen?.brennstoff_1.primaerenergiefaktor.toString(), Math.round(berechnungen?.energieVerbrauchGesamt_1 || 0).toString(), "0", berechnungen?.endEnergieVerbrauchKuehlungsZuschlag_1, Math.round(berechnungen?.energieVerbrauchHeizung_1 || 0).toString(), berechnungen?.durchschnittsKlimafaktor.toString(), berechnungen?.energieVerbrauchStrom ); } else { // Ohne Warmwasserzuschlag addVerbrauch( moment(ausweis.startdatum).format("MM.YYYY"), moment(ausweis.startdatum).add(3, "years").format("MM.YYYY"), aufnahme.brennstoff_1 || "", berechnungen?.brennstoff_1.primaerenergiefaktor.toString(), Math.round(berechnungen?.energieVerbrauchGesamt_1 || 0).toString(), Math.round(berechnungen?.energieVerbrauchWarmwasser_1 || 0).toString(), berechnungen?.endEnergieVerbrauchKuehlungsZuschlag_1, Math.round(berechnungen?.energieVerbrauchHeizung_1 || 0).toString(), berechnungen?.durchschnittsKlimafaktor.toString(), berechnungen?.energieVerbrauchStrom ); } if (ausweis.zusaetzliche_heizquelle) { addVerbrauch( moment(ausweis.startdatum).format("MM.YYYY"), moment(ausweis.startdatum).add(3, "years").format("MM.YYYY"), aufnahme.brennstoff_2 || "", berechnungen?.brennstoff_2.primaerenergiefaktor.toString(), Math.round(berechnungen?.energieVerbrauchGesamt_2 || 0).toString(), Math.round(berechnungen?.energieVerbrauchWarmwasser_2 || 0).toString(), berechnungen?.endEnergieVerbrauchKuehlungsZuschlag_2, Math.round(berechnungen?.energieVerbrauchHeizung_2 || 0).toString(), berechnungen?.durchschnittsKlimafaktor.toString(), 0 ); } /* -------------------------------- Seite 4 -------------------------------- */ const splitToSize = (text: string, size: number, font: PDFFont, fontSize: number) => { const lines = [] let currentLine = "" for (const char of text) { if (font.widthOfTextAtSize(currentLine + char, fontSize) <= size) { currentLine += char; } else { lines.push(currentLine) currentLine = char; } } lines.push(currentLine) return lines.join("\n") } const addEmpfehlungenGenerator = () => { let i = 0; let yOffset = 43; const initialHeight = 562 const initialXOffset = 36; return (bauteil?: string, beschreibung?: string, alsEinzelmassnahme?: boolean, amortisationszeit?: string, kosten?: string) => { pages[3].drawText((i + 1).toString(), { x: initialXOffset, y: initialHeight - (i * yOffset), size: 8, font }) pages[3].drawText(splitToSize(bauteil || "", 70, font, 8), { x: initialXOffset + 25, y: initialHeight - (i * yOffset), size: 8, font, lineHeight: 10 }) pages[3].drawText(splitToSize(beschreibung || "", 230, font, 8), { x: initialXOffset + 98, y: initialHeight - (i * yOffset), size: 8, font, lineHeight: 10 }) pages[3].drawText(amortisationszeit || "", { x: initialXOffset + 403, y: initialHeight - (i * yOffset), size: 8, font }) pages[3].drawText(kosten || "", { x: initialXOffset + 451, y: initialHeight - (i * yOffset), size: 8, font }) i++; } } const addEmpfehlung = addEmpfehlungenGenerator() for (const empfehlung of empfehlungen) { addEmpfehlung(empfehlung.anlagenteil, empfehlung.description, true, empfehlung.amortisationszeit, empfehlung.kosten) } for (const page of pages) { addAnsichtsausweisLabel(page, font) addDatumGEG(page, font) } // pdf.getForm().flatten() return pdf.save(); }