feat: support rich multi-paragraph notes with formatting (Closes #7)

Add {start_of_notes}/{end_of_notes} (and short forms {son}/{eon}) block
directives to ChordProParser for multi-paragraph note content. Blank lines
within the block separate paragraphs. The renderer now word-wraps note
paragraphs to fit within the content width. MeasurementEngine estimates
wrapped line count for more accurate height calculations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #11.
This commit is contained in:
shahondin1624
2026-03-17 09:38:25 +01:00
parent 0139327034
commit 8c92c7d78b
4 changed files with 166 additions and 13 deletions

View File

@@ -58,11 +58,23 @@ class MeasurementEngine(
heightMm += config.layout.verseSpacing
}
// Notes at bottom
// Notes at bottom (with word-wrap estimation for multi-paragraph notes)
if (song.notes.isNotEmpty()) {
heightMm += 1.5f // gap
for (note in song.notes) {
heightMm += fontMetrics.measureLineHeight(config.fonts.metadata, config.fonts.metadata.size) * 1.5f
heightMm += 1.5f // gap before notes
val metaLineHeight = fontMetrics.measureLineHeight(config.fonts.metadata, config.fonts.metadata.size) * 1.5f
// A5 content width in mm = 148 - inner margin - outer margin
val contentWidthMm = 148f - config.layout.margins.inner - config.layout.margins.outer
for ((idx, note) in song.notes.withIndex()) {
// Estimate how many wrapped lines this note paragraph needs
val noteWidthMm = fontMetrics.measureTextWidth(note, config.fonts.metadata, config.fonts.metadata.size)
val estimatedLines = maxOf(1, kotlin.math.ceil((noteWidthMm / contentWidthMm).toDouble()).toInt())
heightMm += metaLineHeight * estimatedLines
// Paragraph spacing between note paragraphs
if (idx < song.notes.size - 1) {
heightMm += metaLineHeight * 0.3f
}
}
}