feat: display reference book page numbers in song page footer (Closes #3)
Render a two-row footer at the bottom of each song page showing reference book abbreviations as column headers with corresponding page numbers below. A thin separator line is drawn above the footer. MeasurementEngine now reserves vertical space for the reference footer when reference books are configured and the song has references. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -233,6 +233,87 @@ class PdfBookRenderer : BookRenderer {
|
||||
y -= metaSize * 1.5f
|
||||
}
|
||||
}
|
||||
|
||||
// Render reference book footer on the last page of the song
|
||||
if (config.referenceBooks.isNotEmpty() && song.references.isNotEmpty()) {
|
||||
val isLastPage = (pageIndex == 0) // For now, all content renders on page 0
|
||||
if (isLastPage) {
|
||||
renderReferenceFooter(
|
||||
cb, fontMetrics, config, song,
|
||||
leftMargin, contentWidth,
|
||||
config.layout.margins.bottom / 0.3528f
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders reference book abbreviations and page numbers as a footer row
|
||||
* at the bottom of the song page, above the page number.
|
||||
*/
|
||||
private fun renderReferenceFooter(
|
||||
cb: PdfContentByte,
|
||||
fontMetrics: PdfFontMetrics,
|
||||
config: BookConfig,
|
||||
song: Song,
|
||||
leftMargin: Float,
|
||||
contentWidth: Float,
|
||||
bottomMargin: Float
|
||||
) {
|
||||
val metaFont = fontMetrics.getBaseFont(config.fonts.metadata)
|
||||
val metaSize = config.fonts.metadata.size
|
||||
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
|
||||
|
||||
// Calculate column widths: evenly distribute across content width
|
||||
val colWidth = contentWidth / books.size
|
||||
|
||||
// Row 1: Abbreviation headers
|
||||
for ((i, book) in books.withIndex()) {
|
||||
val x = leftMargin + i * colWidth
|
||||
val abbr = book.abbreviation
|
||||
val textWidth = metaFont.getWidthPoint(abbr, metaSize)
|
||||
// Center text in column
|
||||
val textX = x + (colWidth - textWidth) / 2
|
||||
|
||||
cb.beginText()
|
||||
cb.setFontAndSize(metaFont, metaSize)
|
||||
cb.setColorFill(Color.DARK_GRAY)
|
||||
cb.setTextMatrix(textX, footerY + lineHeight)
|
||||
cb.showText(abbr)
|
||||
cb.endText()
|
||||
}
|
||||
|
||||
// Row 2: Page numbers
|
||||
for ((i, book) in books.withIndex()) {
|
||||
val x = leftMargin + i * colWidth
|
||||
val pageNum = song.references[book.id]
|
||||
if (pageNum != null) {
|
||||
val pageText = pageNum.toString()
|
||||
val textWidth = metaFont.getWidthPoint(pageText, metaSize)
|
||||
val textX = x + (colWidth - textWidth) / 2
|
||||
|
||||
cb.beginText()
|
||||
cb.setFontAndSize(metaFont, metaSize)
|
||||
cb.setColorFill(Color.DARK_GRAY)
|
||||
cb.setTextMatrix(textX, footerY)
|
||||
cb.showText(pageText)
|
||||
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 renderFillerImage(document: Document, imagePath: String, pageSize: Rectangle) {
|
||||
|
||||
Reference in New Issue
Block a user