feat: support custom font files for song titles (Closes #4)
- Improve PdfFontMetrics: use canonical path for cache key, validate font file existence, use absolute paths for BaseFont.createFont - Add font file path resolution in SongbookPipeline (relative to project directory) - Add font file existence validation in Validator.validateConfig - Add end-to-end tests: custom font loading, umlaut rendering, cache deduplication, missing file error - Document custom font file usage in example songbook.yaml Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #14.
This commit is contained in:
@@ -28,9 +28,12 @@ class SongbookPipeline(private val projectDir: File) {
|
||||
return BuildResult(false, errors = listOf(ValidationError(configFile.name, null, "songbook.yaml not found")))
|
||||
}
|
||||
logger.info { "Parsing config: ${configFile.absolutePath}" }
|
||||
val config = ConfigParser.parse(configFile)
|
||||
val rawConfig = ConfigParser.parse(configFile)
|
||||
|
||||
// Validate config
|
||||
// Resolve font file paths relative to the project directory
|
||||
val config = resolveFontPaths(rawConfig)
|
||||
|
||||
// Validate config (including font file existence)
|
||||
val configErrors = Validator.validateConfig(config)
|
||||
if (configErrors.isNotEmpty()) {
|
||||
return BuildResult(false, errors = configErrors)
|
||||
@@ -149,13 +152,39 @@ class SongbookPipeline(private val projectDir: File) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves font file paths relative to the project directory.
|
||||
* If a FontSpec has a `file` property, it is resolved against projectDir
|
||||
* to produce an absolute path.
|
||||
*/
|
||||
private fun resolveFontPaths(config: BookConfig): BookConfig {
|
||||
fun FontSpec.resolveFile(): FontSpec {
|
||||
val fontFile = this.file ?: return this
|
||||
val fontFileObj = File(fontFile)
|
||||
// Only resolve relative paths; absolute paths are left as-is
|
||||
if (fontFileObj.isAbsolute) return this
|
||||
val resolved = File(projectDir, fontFile)
|
||||
return this.copy(file = resolved.absolutePath)
|
||||
}
|
||||
|
||||
val resolvedFonts = config.fonts.copy(
|
||||
lyrics = config.fonts.lyrics.resolveFile(),
|
||||
chords = config.fonts.chords.resolveFile(),
|
||||
title = config.fonts.title.resolveFile(),
|
||||
metadata = config.fonts.metadata.resolveFile(),
|
||||
toc = config.fonts.toc.resolveFile()
|
||||
)
|
||||
return config.copy(fonts = resolvedFonts)
|
||||
}
|
||||
|
||||
fun validate(): List<ValidationError> {
|
||||
val configFile = File(projectDir, "songbook.yaml")
|
||||
if (!configFile.exists()) {
|
||||
return listOf(ValidationError(configFile.name, null, "songbook.yaml not found"))
|
||||
}
|
||||
|
||||
val config = ConfigParser.parse(configFile)
|
||||
val rawConfig = ConfigParser.parse(configFile)
|
||||
val config = resolveFontPaths(rawConfig)
|
||||
val errors = mutableListOf<ValidationError>()
|
||||
errors.addAll(Validator.validateConfig(config))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user