fix: position reference footer below content to prevent overlap (Closes #25)
The reference footer was anchored to a fixed position near the bottom margin, causing it to overlap with notes text on songs that had both notes and references (e.g., "Die Gedanken sind frei"). Changed renderReferenceFooter to use flow-based positioning from the current y-position after notes/metadata, ensuring the separator line, abbreviation headers, and page numbers always render below the preceding content without overlap. Also updated calculateFooterReservation and MeasurementEngine to match the simplified layout calculation, and added toc.highlight_column to the example songbook.yaml to demonstrate column highlighting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -91,11 +91,11 @@ class MeasurementEngine(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference book footer: reserve space for abbreviation row + page number row + separator line
|
// Reference book footer: gap + separator line + abbreviation row + page number row
|
||||||
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty()) {
|
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty()) {
|
||||||
val metaLineHeight = fontMetrics.measureLineHeight(config.fonts.metadata, config.fonts.metadata.size)
|
val metaLineHeight = fontMetrics.measureLineHeight(config.fonts.metadata, config.fonts.metadata.size)
|
||||||
|
heightMm += 4f * 0.3528f // gap before footer (4pt converted to mm)
|
||||||
heightMm += metaLineHeight * 1.4f * 2 // two rows (headers + numbers)
|
heightMm += metaLineHeight * 1.4f * 2 // two rows (headers + numbers)
|
||||||
heightMm += metaLineHeight * 0.5f // separator line gap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val pageCount = if (heightMm <= contentHeightMm) 1 else 2
|
val pageCount = if (heightMm <= contentHeightMm) 1 else 2
|
||||||
|
|||||||
@@ -240,12 +240,11 @@ class PdfBookRenderer : BookRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference footer
|
// Reference footer: gap + separator line + abbreviation row + page number row
|
||||||
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty()) {
|
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty()) {
|
||||||
val lineHeight = metaSize * 1.4f
|
val lineHeight = metaSize * 1.4f
|
||||||
|
reserved += 4f // gap before footer
|
||||||
reserved += lineHeight * 2 // two rows (headers + numbers)
|
reserved += lineHeight * 2 // two rows (headers + numbers)
|
||||||
reserved += lineHeight * 1.5f // separator line gap
|
|
||||||
reserved += lineHeight * 0.5f // bottom gap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return reserved
|
return reserved
|
||||||
@@ -474,10 +473,10 @@ class PdfBookRenderer : BookRenderer {
|
|||||||
|
|
||||||
// Render reference book footer on the last page of the song
|
// Render reference book footer on the last page of the song
|
||||||
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty() && isLastPage) {
|
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty() && isLastPage) {
|
||||||
|
y -= 4f // gap before reference footer
|
||||||
renderReferenceFooter(
|
renderReferenceFooter(
|
||||||
cb, fontMetrics, config, song,
|
cb, fontMetrics, config, song,
|
||||||
leftMargin, contentWidth,
|
leftMargin, y, contentWidth
|
||||||
bottomMargin
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -512,8 +511,9 @@ class PdfBookRenderer : BookRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders reference book abbreviations and page numbers as a footer row
|
* Renders reference book abbreviations and page numbers as a footer
|
||||||
* at the bottom of the song page, above the page number.
|
* on the song page, positioned below the current content at [topY].
|
||||||
|
* Layout (top to bottom): separator line, abbreviation row, page number row.
|
||||||
*/
|
*/
|
||||||
private fun renderReferenceFooter(
|
private fun renderReferenceFooter(
|
||||||
cb: PdfContentByte,
|
cb: PdfContentByte,
|
||||||
@@ -521,40 +521,44 @@ class PdfBookRenderer : BookRenderer {
|
|||||||
config: BookConfig,
|
config: BookConfig,
|
||||||
song: Song,
|
song: Song,
|
||||||
leftMargin: Float,
|
leftMargin: Float,
|
||||||
contentWidth: Float,
|
topY: Float,
|
||||||
bottomMargin: Float
|
contentWidth: Float
|
||||||
) {
|
) {
|
||||||
val metaFont = fontMetrics.getBaseFont(config.fonts.metadata)
|
val metaFont = fontMetrics.getBaseFont(config.fonts.metadata)
|
||||||
val metaSize = config.fonts.metadata.size
|
val metaSize = config.fonts.metadata.size
|
||||||
val lineHeight = metaSize * 1.4f
|
val lineHeight = metaSize * 1.4f
|
||||||
|
|
||||||
// Position: just above the page number area
|
|
||||||
val footerY = bottomMargin + lineHeight * 0.5f
|
|
||||||
|
|
||||||
// Map book IDs to abbreviations
|
|
||||||
val refAbbreviations = config.referenceBooks.associate { it.id to it.abbreviation }
|
|
||||||
val books = config.referenceBooks
|
val books = config.referenceBooks
|
||||||
|
|
||||||
// Calculate column widths: evenly distribute across content width
|
// Calculate column widths: evenly distribute across content width
|
||||||
val colWidth = contentWidth / books.size
|
val colWidth = contentWidth / books.size
|
||||||
|
|
||||||
// Row 1: Abbreviation headers
|
// Draw a thin separator line at the top of the footer
|
||||||
|
val lineY = topY
|
||||||
|
cb.setLineWidth(0.3f)
|
||||||
|
cb.setColorStroke(Color.LIGHT_GRAY)
|
||||||
|
cb.moveTo(leftMargin, lineY)
|
||||||
|
cb.lineTo(leftMargin + contentWidth, lineY)
|
||||||
|
cb.stroke()
|
||||||
|
|
||||||
|
// Row 1: Abbreviation headers (below the separator line)
|
||||||
|
val abbrY = lineY - lineHeight
|
||||||
for ((i, book) in books.withIndex()) {
|
for ((i, book) in books.withIndex()) {
|
||||||
val x = leftMargin + i * colWidth
|
val x = leftMargin + i * colWidth
|
||||||
val abbr = book.abbreviation
|
val abbr = book.abbreviation
|
||||||
val textWidth = metaFont.getWidthPoint(abbr, metaSize)
|
val textWidth = metaFont.getWidthPoint(abbr, metaSize)
|
||||||
// Center text in column
|
|
||||||
val textX = x + (colWidth - textWidth) / 2
|
val textX = x + (colWidth - textWidth) / 2
|
||||||
|
|
||||||
cb.beginText()
|
cb.beginText()
|
||||||
cb.setFontAndSize(metaFont, metaSize)
|
cb.setFontAndSize(metaFont, metaSize)
|
||||||
cb.setColorFill(Color.DARK_GRAY)
|
cb.setColorFill(Color.DARK_GRAY)
|
||||||
cb.setTextMatrix(textX, footerY + lineHeight)
|
cb.setTextMatrix(textX, abbrY)
|
||||||
cb.showText(abbr)
|
cb.showText(abbr)
|
||||||
cb.endText()
|
cb.endText()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Row 2: Page numbers
|
// Row 2: Page numbers (below abbreviation headers)
|
||||||
|
val numY = abbrY - lineHeight
|
||||||
for ((i, book) in books.withIndex()) {
|
for ((i, book) in books.withIndex()) {
|
||||||
val x = leftMargin + i * colWidth
|
val x = leftMargin + i * colWidth
|
||||||
val pageNum = song.references[book.id]
|
val pageNum = song.references[book.id]
|
||||||
@@ -566,18 +570,11 @@ class PdfBookRenderer : BookRenderer {
|
|||||||
cb.beginText()
|
cb.beginText()
|
||||||
cb.setFontAndSize(metaFont, metaSize)
|
cb.setFontAndSize(metaFont, metaSize)
|
||||||
cb.setColorFill(Color.DARK_GRAY)
|
cb.setColorFill(Color.DARK_GRAY)
|
||||||
cb.setTextMatrix(textX, footerY)
|
cb.setTextMatrix(textX, numY)
|
||||||
cb.showText(pageText)
|
cb.showText(pageText)
|
||||||
cb.endText()
|
cb.endText()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a thin line above the footer
|
|
||||||
cb.setLineWidth(0.3f)
|
|
||||||
cb.setColorStroke(Color.LIGHT_GRAY)
|
|
||||||
cb.moveTo(leftMargin, footerY + lineHeight * 1.5f)
|
|
||||||
cb.lineTo(leftMargin + contentWidth, footerY + lineHeight * 1.5f)
|
|
||||||
cb.stroke()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderForewordPage(
|
private fun renderForewordPage(
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ reference_books:
|
|||||||
name: "Pfadfinderliederbuch"
|
name: "Pfadfinderliederbuch"
|
||||||
abbreviation: "PfLB"
|
abbreviation: "PfLB"
|
||||||
|
|
||||||
|
toc:
|
||||||
|
highlight_column: "Seite"
|
||||||
|
|
||||||
output:
|
output:
|
||||||
directory: "./output"
|
directory: "./output"
|
||||||
filename: "liederbuch.pdf"
|
filename: "liederbuch.pdf"
|
||||||
|
|||||||
Reference in New Issue
Block a user