Files
MarkdownToIHKChemnits/pdf_pages.go
T

250 lines
8.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"fmt"
"sort"
"github.com/go-pdf/fpdf"
)
// RenderTitlePage renders the cover page without a page number.
// Layout follows the IHK Chemnitz template (section 2.2 of the guidelines).
func (r *IHKRenderer) RenderTitlePage() {
r.numType = NumNone
r.pdf.AddPage()
// Use symmetrical margins on the title page for visual centering.
r.pdf.SetLeftMargin(dinMarginLeft)
r.pdf.SetRightMargin(dinMarginLeft)
defer func() {
r.pdf.SetLeftMargin(dinMarginLeft)
r.pdf.SetRightMargin(dinMarginRight)
}()
// Exam type and profession
r.pdf.SetFont("Helvetica", "B", 16)
r.pdf.CellFormat(0, 12, r.tr("Abschlussprüfung zum"), "", 1, "C", false, 0, "")
r.pdf.CellFormat(0, 12, r.tr(r.config.Student.Profession), "", 1, "C", false, 0, "")
r.pdf.Ln(20)
r.pdf.SetFont("Helvetica", "", dinFontBody)
r.pdf.CellFormat(0, dinLineHtBody, r.tr("Projektarbeit von"), "", 1, "C", false, 0, "")
r.pdf.Ln(4)
// Candidate name — bold, 14 pt
r.pdf.SetFont("Helvetica", "B", dinFontHeading)
r.pdf.CellFormat(0, dinLineHtHeading, r.tr(r.config.Student.Name), "", 1, "C", false, 0, "")
r.pdf.Ln(20)
// Project title — bold, 16 pt, centred, word-wrapped
r.pdf.SetFont("Helvetica", "B", 16)
r.pdf.MultiCell(0, 10, r.tr(r.config.Project.Title), "", "C", false)
if r.config.Project.Subtitle != "" {
r.pdf.Ln(4)
r.pdf.SetFont("Helvetica", "I", dinFontBody)
r.pdf.MultiCell(0, dinLineHtBody, r.tr(r.config.Project.Subtitle), "", "C", false)
}
// Bottom block: exam period, training company, supervisor
r.pdf.SetY(-80)
r.pdf.SetFont("Helvetica", "", dinFontBody)
labelW := 60.0
r.pdf.CellFormat(labelW, dinLineHtBody, r.tr("Prüfungsperiode:"), "", 0, "L", false, 0, "")
r.pdf.MultiCell(0, dinLineHtBody, r.tr(r.config.Project.Period), "", "L", false)
r.pdf.CellFormat(labelW, dinLineHtBody, r.tr("Ausbildungsbetrieb:"), "", 0, "L", false, 0, "")
r.pdf.MultiCell(0, dinLineHtBody, r.tr(r.config.Student.Company), "", "L", false)
r.pdf.CellFormat(labelW, dinLineHtBody, r.tr("Projektbetreuer:"), "", 0, "L", false, 0, "")
r.pdf.MultiCell(0, dinLineHtBody, r.tr(r.config.Student.Supervisor), "", "L", false)
}
// RenderDeclarationPage renders the declaration of authenticity without a page
// number, as required by IHK Chemnitz (section 2.12 of the guidelines).
// This page must be physically inserted in every submitted copy.
func (r *IHKRenderer) RenderDeclarationPage() {
r.numType = NumNone
r.pdf.AddPage()
r.pdf.SetFont("Helvetica", "B", dinFontHeading)
r.pdf.CellFormat(0, dinLineHtHeading+4, r.tr("Erklärung"), "", 1, "C", false, 0, "")
r.pdf.Ln(dinSpaceAfterHeading)
// Legally required declaration text — must remain in German.
r.pdf.SetFont("Helvetica", "", dinFontBody)
declaration := fmt.Sprintf(
"Ich versichere durch meine Unterschrift, dass ich diese Projektarbeit"+
" mit dem Thema \"%s\" selbstständig, ohne fremde Hilfe angefertigt,"+
" alle Stellen, die ich wörtlich oder annähernd wörtlich aus"+
" Veröffentlichungen entnommen, als solche kenntlich gemacht und mich"+
" auch keiner anderen als der angegebenen Literatur oder sonstiger"+
" Hilfsmittel bedient habe. Die Projektarbeit hat in dieser oder"+
" ähnlicher Form weder der Industrie- und Handelskammer Chemnitz noch"+
" einer anderen Prüfungsinstitution vorgelegen.",
r.config.Project.Title,
)
r.pdf.MultiCell(0, dinLineHtBody, r.tr(declaration), "", "J", false)
r.pdf.Ln(dinLineHtBody)
additional := "Mir ist bekannt, dass gemäß der Prüfungsordnung für die Durchführung" +
" von Abschlussprüfungen der Industrie- und Handelskammer Chemnitz" +
" Täuschungshandlungen zum Ausschluss von der Prüfung führen können und" +
" die Prüfung als nicht bestanden erklärt werden kann."
r.pdf.MultiCell(0, dinLineHtBody, r.tr(additional), "", "J", false)
r.pdf.Ln(20)
r.pdf.CellFormat(0, dinLineHtBody, r.tr("Ort, Datum, Unterschrift (mit Vor- und Nachnamen)"), "", 1, "L", false, 0, "")
r.pdf.Ln(4)
// Signature line
r.pdf.SetDrawColor(0, 0, 0)
x, y := r.pdf.GetXY()
r.pdf.Line(x, y, x+80, y)
}
// RenderBibliography renders the bibliography sorted alphabetically per IHK
// rule 2.8.1. Skipped when no @Quelle directives were found in the document.
func (r *IHKRenderer) RenderBibliography() {
if len(r.sources) == 0 {
return
}
r.RenderHeader(1, "Literaturverzeichnis")
sorted := make([]string, len(r.sources))
copy(sorted, r.sources)
sort.Strings(sorted)
r.pdf.SetFont("Helvetica", "", dinFontBody)
for _, source := range sorted {
r.pdf.MultiCell(0, dinLineHtBody, r.tr(" "+source), "", "J", false)
r.pdf.Ln(2)
}
}
// RenderAppendices renders the appendix section followed by individual annex
// pages. The opening page contains the index of all annexes. Each annex image
// is scaled to fill the available page area.
func (r *IHKRenderer) RenderAppendices() {
if len(r.appendices) == 0 {
return
}
r.RenderHeader(1, "Anhang")
// Annex index
r.pdf.SetFont("Helvetica", "B", dinFontBody)
r.pdf.CellFormat(0, dinLineHtBody, r.tr("Anlagenverzeichnis"), "", 1, "L", false, 0, "")
r.pdf.Ln(dinSpaceAfterHeading)
r.pdf.SetFont("Helvetica", "", dinFontBody)
for i, app := range r.appendices {
r.pdf.CellFormat(0, dinLineHtBody,
r.tr(fmt.Sprintf("Anlage %d: %s", i+1, app.Title)), "", 1, "L", false, 0, "")
}
// Individual annex pages
for i, app := range r.appendices {
r.pdf.AddPage()
r.pdf.SetFont("Helvetica", "B", dinFontBody)
r.pdf.CellFormat(0, dinLineHtBody,
r.tr(fmt.Sprintf("Anlage %d: %s", i+1, app.Title)), "", 1, "L", false, 0, "")
r.pdf.Ln(dinSpaceAfterHeading)
r.renderAppendixImage(app.Path)
}
}
// renderAppendixImage scales and embeds an image to fill the remaining page area.
func (r *IHKRenderer) renderAppendixImage(path string) {
info := r.ensureImageRegistered(path)
if info == nil {
r.pdf.CellFormat(0, dinLineHtBody, r.tr("[Image could not be loaded: "+path+"]"),
"1", 1, "C", false, 0, "")
return
}
_, pageH := r.pdf.GetPageSize()
lm, _, _, bm := r.pdf.GetMargins()
uw := r.usableWidth()
availH := pageH - r.pdf.GetY() - bm - 10
imgW := info.Width() * ptToMM
imgH := info.Height() * ptToMM
// Scale to fill available width, then clamp to available height.
displayW := uw
displayH := imgH * (displayW / imgW)
if displayH > availH {
scale := availH / displayH
displayH = availH
displayW = displayW * scale
}
posX := lm + (uw-displayW)/2
r.pdf.ImageOptions(path, posX, r.pdf.GetY(), displayW, displayH, false,
fpdf.ImageOptions{ReadDpi: true}, 0, "")
}
// RenderAbbreviations renders the list of abbreviations from the YAML config.
// It is placed after the TOC and uses Roman page numbering.
func (r *IHKRenderer) RenderAbbreviations() {
if len(r.config.Abbreviations) == 0 {
return
}
r.pdf.AddPage()
r.RecordHeader(1, "Abkürzungsverzeichnis")
r.pdf.SetFont("Helvetica", "B", dinFontHeading)
r.pdf.CellFormat(0, dinLineHtHeading+4, r.tr("Abkürzungsverzeichnis"), "", 1, "L", false, 0, "")
r.pdf.Ln(dinSpaceAfterHeading)
uw := r.usableWidth()
abbrColW := 40.0
meaningColW := uw - abbrColW
// Header row with grey fill
r.pdf.SetFillColor(230, 230, 230)
r.pdf.SetFont("Helvetica", "B", dinFontBody)
r.pdf.CellFormat(abbrColW, dinLineHtBody, r.tr("Abkürzung"), "1", 0, "L", true, 0, "")
r.pdf.CellFormat(meaningColW, dinLineHtBody, r.tr("Bedeutung"), "1", 1, "L", true, 0, "")
r.pdf.SetFillColor(255, 255, 255)
r.pdf.SetFont("Helvetica", "", dinFontBody)
for _, a := range r.config.Abbreviations {
r.pdf.CellFormat(abbrColW, dinLineHtBody, r.tr(a.Abbr), "1", 0, "L", false, 0, "")
r.pdf.CellFormat(meaningColW, dinLineHtBody, r.tr(a.Meaning), "1", 1, "L", false, 0, "")
}
}
// RenderGlossary renders the glossary from the YAML config.
// It is placed after the appendices and uses Arabic page numbering.
func (r *IHKRenderer) RenderGlossary() {
if len(r.config.Glossary) == 0 {
return
}
r.RenderHeader(1, "Glossar")
for _, entry := range r.config.Glossary {
r.pdf.SetFont("Helvetica", "B", dinFontBody)
r.pdf.CellFormat(0, dinLineHtBody, r.tr(entry.Term), "", 1, "L", false, 0, "")
r.pdf.SetFont("Helvetica", "", dinFontBody)
r.pdf.MultiCell(0, dinLineHtBody, r.tr(entry.Definition), "", "J", false)
r.pdf.Ln(4)
}
}
// ensureImageRegistered registers an image with fpdf if not already known
// and returns its metadata. Returns nil if the image cannot be loaded.
func (r *IHKRenderer) ensureImageRegistered(path string) *fpdf.ImageInfoType {
info := r.pdf.GetImageInfo(path)
if info == nil {
r.pdf.RegisterImageOptions(path, fpdf.ImageOptions{ReadDpi: true})
info = r.pdf.GetImageInfo(path)
}
return info
}
// ptToMM converts PDF points to millimetres (1 pt = 25.4 / 72 mm).
const ptToMM = 25.4 / 72.0