feat: anchor footer elements to bottom of page (Closes #31) #32

Merged
shahondin1624 merged 1 commits from feature/issue-31-anchor-footer-elements-to-bottom-of-page into main 2026-03-17 16:21:00 +01:00
Showing only changes of commit 17d306e822 - Show all commits

View File

@@ -424,62 +424,70 @@ class PdfBookRenderer : BookRenderer {
y -= config.layout.verseSpacing / 0.3528f
}
// Render notes on the last page
if (isLastPage && song.notes.isNotEmpty()) {
y -= 4f
// Render footer elements (notes, metadata, references) anchored to the bottom of the page.
// Instead of flowing from the current y position after song content, we compute a fixed
// starting Y at the top of the footer area (bottomMargin + footerReservation) and render
// top-down: notes -> metadata -> references. This ensures footer elements always appear
// at the same vertical position regardless of how much song content is on the page.
if (isLastPage && footerReservation > 0f) {
val metaFont = fontMetrics.getBaseFont(config.fonts.metadata)
val metaSize = config.fonts.metadata.size
val noteLineHeight = metaSize * 1.5f
// The footer area spans from bottomMargin to bottomMargin + footerReservation.
// Start rendering from the top of this area, flowing downward.
var footerY = bottomMargin + footerReservation
// Render notes (topmost footer element)
if (song.notes.isNotEmpty()) {
footerY -= 4f // gap before notes
val noteLineHeight = metaSize * 1.5f
for ((idx, note) in song.notes.withIndex()) {
val wrappedLines = wrapText(note, metaFont, metaSize, contentWidth)
for (wrappedLine in wrappedLines) {
cb.beginText()
cb.setFontAndSize(metaFont, metaSize)
cb.setColorFill(Color.GRAY)
cb.setTextMatrix(leftMargin, y - metaSize)
cb.setTextMatrix(leftMargin, footerY - metaSize)
cb.showText(wrappedLine)
cb.endText()
y -= noteLineHeight
footerY -= noteLineHeight
}
// Add paragraph spacing between note paragraphs
if (idx < song.notes.size - 1) {
y -= noteLineHeight * 0.3f
footerY -= noteLineHeight * 0.3f
}
}
}
// Render metadata at bottom of song page (if configured) - on the last page only
if (renderMetaAtBottom && isLastPage) {
// Render metadata (Worte/Weise) below notes, if configured at bottom
if (renderMetaAtBottom) {
val metaParts = buildMetadataLines(song, config)
if (metaParts.isNotEmpty()) {
y -= 4f
val metaFont = fontMetrics.getBaseFont(config.fonts.metadata)
val metaSize = config.fonts.metadata.size
footerY -= 4f // gap before metadata
for (metaLine in metaParts) {
val wrappedLines = wrapText(metaLine, metaFont, metaSize, contentWidth)
for (wrappedLine in wrappedLines) {
cb.beginText()
cb.setFontAndSize(metaFont, metaSize)
cb.setColorFill(Color.GRAY)
cb.setTextMatrix(leftMargin, y - metaSize)
cb.setTextMatrix(leftMargin, footerY - metaSize)
cb.showText(wrappedLine)
cb.endText()
y -= metaSize * 1.5f
footerY -= metaSize * 1.5f
}
}
}
}
// Render reference book footer on the last page of the song
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty() && isLastPage) {
y -= 4f // gap before reference footer
// Render reference book footer (bottommost footer element, just above page number)
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty()) {
footerY -= 4f // gap before reference footer
renderReferenceFooter(
cb, fontMetrics, config, song,
leftMargin, y, contentWidth
leftMargin, footerY, contentWidth
)
}
}
}
/**
* Build metadata lines based on configured label style.