feat: add support for foreword/preface pages (Closes #1)

Add ForewordConfig to BookConfig, Foreword model type, ForewordParser for
text files (quote/paragraphs/signatures), ForewordPage in PageContent,
pipeline integration to insert foreword after TOC, and PDF rendering with
styled quote, horizontal rule separator, word-wrapped paragraphs, and
right-aligned signatures.

Also adds Gradle wrapper and adjusts build toolchain for JDK 25 compat.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #8.
This commit is contained in:
shahondin1624
2026-03-17 09:27:00 +01:00
parent e386501b57
commit 8e4728c55a
14 changed files with 792 additions and 7 deletions

View File

@@ -2,6 +2,7 @@ package de.pfadfinder.songbook.app
import de.pfadfinder.songbook.model.*
import de.pfadfinder.songbook.parser.*
import de.pfadfinder.songbook.parser.ForewordParser
import de.pfadfinder.songbook.layout.*
import de.pfadfinder.songbook.renderer.pdf.*
import mu.KotlinLogging
@@ -80,6 +81,19 @@ class SongbookPipeline(private val projectDir: File) {
logger.info { "Parsed ${sortedSongs.size} songs" }
// 2b. Parse foreword (if configured)
var foreword: Foreword? = null
val forewordConfig = config.foreword
if (forewordConfig != null) {
val forewordFile = File(projectDir, forewordConfig.file)
if (forewordFile.exists()) {
logger.info { "Parsing foreword: ${forewordFile.absolutePath}" }
foreword = ForewordParser.parseFile(forewordFile)
} else {
logger.warn { "Foreword file not found: ${forewordFile.absolutePath}" }
}
}
// 3. Measure songs
val fontMetrics = PdfFontMetrics()
val measurementEngine = MeasurementEngine(fontMetrics, config)
@@ -89,14 +103,25 @@ class SongbookPipeline(private val projectDir: File) {
val tocGenerator = TocGenerator(config)
val tocPages = tocGenerator.estimateTocPages(sortedSongs)
val paginationEngine = PaginationEngine(config)
val pages = paginationEngine.paginate(measuredSongs, tocPages)
// Foreword always takes 2 pages (for double-sided printing)
val forewordPages = if (foreword != null) 2 else 0
val tocEntries = tocGenerator.generate(pages, tocPages)
val paginationEngine = PaginationEngine(config)
val pages = paginationEngine.paginate(measuredSongs, tocPages + forewordPages)
val tocEntries = tocGenerator.generate(pages, tocPages + forewordPages)
// Build final page list with foreword pages inserted before song content
val allPages = mutableListOf<PageContent>()
if (foreword != null) {
allPages.add(PageContent.ForewordPage(foreword, 0))
allPages.add(PageContent.ForewordPage(foreword, 1))
}
allPages.addAll(pages)
val layoutResult = LayoutResult(
tocPages = tocPages,
pages = pages,
pages = allPages,
tocEntries = tocEntries
)