From 0139327034f064d682775a117aa1a23c0eafe309 Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Tue, 17 Mar 2026 09:35:27 +0100 Subject: [PATCH] feat: highlight the current book's column in the TOC (Closes #6) Add TocConfig with highlightColumn field to BookConfig. TocRenderer now applies a light gray background shading to the designated column header and data cells, making it easy to visually distinguish the current book's page numbers from reference book columns. Co-Authored-By: Claude Opus 4.6 --- .../pfadfinder/songbook/model/BookConfig.kt | 7 ++- .../songbook/parser/ConfigParserTest.kt | 22 +++++++++ .../songbook/renderer/pdf/TocRenderer.kt | 49 +++++++++++++++---- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/model/src/main/kotlin/de/pfadfinder/songbook/model/BookConfig.kt b/model/src/main/kotlin/de/pfadfinder/songbook/model/BookConfig.kt index 1115ad7..a8ff335 100644 --- a/model/src/main/kotlin/de/pfadfinder/songbook/model/BookConfig.kt +++ b/model/src/main/kotlin/de/pfadfinder/songbook/model/BookConfig.kt @@ -8,7 +8,12 @@ data class BookConfig( val images: ImagesConfig = ImagesConfig(), val referenceBooks: List = emptyList(), val output: OutputConfig = OutputConfig(), - val foreword: ForewordConfig? = null + val foreword: ForewordConfig? = null, + val toc: TocConfig = TocConfig() +) + +data class TocConfig( + val highlightColumn: String? = null // abbreviation of the column to highlight (e.g. "CL") ) data class ForewordConfig( diff --git a/parser/src/test/kotlin/de/pfadfinder/songbook/parser/ConfigParserTest.kt b/parser/src/test/kotlin/de/pfadfinder/songbook/parser/ConfigParserTest.kt index 73b66d0..a92935a 100644 --- a/parser/src/test/kotlin/de/pfadfinder/songbook/parser/ConfigParserTest.kt +++ b/parser/src/test/kotlin/de/pfadfinder/songbook/parser/ConfigParserTest.kt @@ -192,6 +192,28 @@ class ConfigParserTest { config.foreword.shouldBeNull() } + @Test + fun `parse config with toc highlight column`() { + val yaml = """ + book: + title: "Test" + toc: + highlight_column: "CL" + """.trimIndent() + val config = ConfigParser.parse(yaml) + config.toc.highlightColumn shouldBe "CL" + } + + @Test + fun `parse config without toc section uses defaults`() { + val yaml = """ + book: + title: "Test" + """.trimIndent() + val config = ConfigParser.parse(yaml) + config.toc.highlightColumn.shouldBeNull() + } + @Test fun `parse config ignores unknown properties`() { val yaml = """ diff --git a/renderer-pdf/src/main/kotlin/de/pfadfinder/songbook/renderer/pdf/TocRenderer.kt b/renderer-pdf/src/main/kotlin/de/pfadfinder/songbook/renderer/pdf/TocRenderer.kt index b3f69bc..b7fb3d0 100644 --- a/renderer-pdf/src/main/kotlin/de/pfadfinder/songbook/renderer/pdf/TocRenderer.kt +++ b/renderer-pdf/src/main/kotlin/de/pfadfinder/songbook/renderer/pdf/TocRenderer.kt @@ -3,11 +3,15 @@ package de.pfadfinder.songbook.renderer.pdf import com.lowagie.text.* import com.lowagie.text.pdf.* import de.pfadfinder.songbook.model.* +import java.awt.Color class TocRenderer( private val fontMetrics: PdfFontMetrics, private val config: BookConfig ) { + // Light gray background for the highlighted column + private val highlightColor = Color(220, 220, 220) + fun render(document: Document, writer: PdfWriter, tocEntries: List) { val tocFont = fontMetrics.getBaseFont(config.fonts.toc) val tocBoldFont = fontMetrics.getBaseFontBold(config.fonts.toc) @@ -35,12 +39,25 @@ class TocRenderer( } table.setWidths(widths) + // Determine which column index should be highlighted + val highlightAbbrev = config.toc.highlightColumn + val highlightColumnIndex: Int? = if (highlightAbbrev != null) { + // Check "Seite" (page) column first - the current book's page number column + if (highlightAbbrev == "Seite") { + 1 + } else { + val refIndex = refBooks.indexOfFirst { it.abbreviation == highlightAbbrev } + if (refIndex >= 0) 2 + refIndex else null + } + } else null + // Header row val headerFont = Font(tocBoldFont, fontSize, Font.BOLD) - table.addCell(headerCell("Titel", headerFont)) - table.addCell(headerCell("Seite", headerFont)) - for (book in refBooks) { - table.addCell(headerCell(book.abbreviation, headerFont)) + table.addCell(headerCell("Titel", headerFont, isHighlighted = false)) + table.addCell(headerCell("Seite", headerFont, isHighlighted = highlightColumnIndex == 1)) + for ((i, book) in refBooks.withIndex()) { + val isHighlighted = highlightColumnIndex == 2 + i + table.addCell(headerCell(book.abbreviation, headerFont, isHighlighted = isHighlighted)) } table.headerRows = 1 @@ -49,31 +66,43 @@ class TocRenderer( val aliasFont = Font(tocFont, fontSize, Font.ITALIC) for (entry in tocEntries.sortedBy { it.title.lowercase() }) { val font = if (entry.isAlias) aliasFont else entryFont - table.addCell(entryCell(entry.title, font)) - table.addCell(entryCell(entry.pageNumber.toString(), entryFont, Element.ALIGN_RIGHT)) - for (book in refBooks) { + table.addCell(entryCell(entry.title, font, isHighlighted = false)) + table.addCell(entryCell(entry.pageNumber.toString(), entryFont, Element.ALIGN_RIGHT, isHighlighted = highlightColumnIndex == 1)) + for ((i, book) in refBooks.withIndex()) { val ref = entry.references[book.abbreviation] - table.addCell(entryCell(ref?.toString() ?: "", entryFont, Element.ALIGN_RIGHT)) + val isHighlighted = highlightColumnIndex == 2 + i + table.addCell(entryCell(ref?.toString() ?: "", entryFont, Element.ALIGN_RIGHT, isHighlighted = isHighlighted)) } } document.add(table) } - private fun headerCell(text: String, font: Font): PdfPCell { + private fun headerCell(text: String, font: Font, isHighlighted: Boolean): PdfPCell { val cell = PdfPCell(Phrase(text, font)) cell.borderWidth = 0f cell.borderWidthBottom = 0.5f cell.paddingBottom = 4f + if (isHighlighted) { + cell.backgroundColor = highlightColor + } return cell } - private fun entryCell(text: String, font: Font, alignment: Int = Element.ALIGN_LEFT): PdfPCell { + private fun entryCell( + text: String, + font: Font, + alignment: Int = Element.ALIGN_LEFT, + isHighlighted: Boolean = false + ): PdfPCell { val cell = PdfPCell(Phrase(text, font)) cell.borderWidth = 0f cell.horizontalAlignment = alignment cell.paddingTop = 1f cell.paddingBottom = 1f + if (isHighlighted) { + cell.backgroundColor = highlightColor + } return cell } } -- 2.49.1