feat: support inline images within song pages (Closes #2)
Add {image: path} directive to embed images at any position within a song's
sections. SongLine gains an optional imagePath field; when set, the line
represents an inline image rather than chord/lyric content. The renderer
scales and centers images within the content width. MeasurementEngine
reserves 40mm height per inline image for layout calculations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #13.
This commit is contained in:
@@ -205,8 +205,14 @@ class PdfBookRenderer : BookRenderer {
|
||||
|
||||
// Render lines
|
||||
for (line in section.lines) {
|
||||
val height = chordLyricRenderer.renderLine(cb, line, leftMargin, y, contentWidth)
|
||||
y -= height + 1f // 1pt gap between lines
|
||||
val imgPath = line.imagePath
|
||||
if (imgPath != null) {
|
||||
// Render inline image
|
||||
y -= renderInlineImage(cb, imgPath, leftMargin, y, contentWidth)
|
||||
} else {
|
||||
val height = chordLyricRenderer.renderLine(cb, line, leftMargin, y, contentWidth)
|
||||
y -= height + 1f // 1pt gap between lines
|
||||
}
|
||||
}
|
||||
|
||||
// End repeat marker
|
||||
@@ -446,6 +452,37 @@ class PdfBookRenderer : BookRenderer {
|
||||
return lines
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an inline image within a song page at the given position.
|
||||
* Returns the total height consumed in PDF points.
|
||||
*/
|
||||
private fun renderInlineImage(
|
||||
cb: PdfContentByte,
|
||||
imagePath: String,
|
||||
leftMargin: Float,
|
||||
y: Float,
|
||||
contentWidth: Float
|
||||
): Float {
|
||||
try {
|
||||
val img = Image.getInstance(imagePath)
|
||||
// Scale to fit within content width, max height 40mm (~113 points)
|
||||
val maxHeight = 40f / 0.3528f // 40mm in points
|
||||
img.scaleToFit(contentWidth * 0.8f, maxHeight)
|
||||
|
||||
// Center horizontally
|
||||
val imgX = leftMargin + (contentWidth - img.scaledWidth) / 2
|
||||
val imgY = y - img.scaledHeight - 3f // 3pt gap above
|
||||
|
||||
img.setAbsolutePosition(imgX, imgY)
|
||||
cb.addImage(img)
|
||||
|
||||
return img.scaledHeight + 6f // image height + gaps above/below
|
||||
} catch (_: Exception) {
|
||||
// If image can't be loaded, consume minimal space
|
||||
return 5f
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderFillerImage(document: Document, imagePath: String, pageSize: Rectangle) {
|
||||
try {
|
||||
val img = Image.getInstance(imagePath)
|
||||
|
||||
Reference in New Issue
Block a user