My inner perfectionist only have one complaint: the note width has been hard coded and the value should be adjusted if non default notehead sizes are used.
I haven't tested this a lot yet so maybe there are bugs. The code boiled down to suprisingly few lines in the end proving the power of Lilypond once you know the commands.
\version "2.12.1"
#(define vowel-set (list->char-set (string->list "AEIOUYÅÄÖaeiouyåäö")))
#(define (width grob text-string)
(let* (
(layout (ly:grob-layout grob))
(props (ly:grob-alist-chain grob (ly:output-def-lookup layout 'text-font-defaults))))
(cdr (ly:stencil-extent (ly:text-interface::interpret-markup layout props (markup text-string)) X))))
#(define (center-on-vowel grob)
(let* ((syllable (ly:grob-property-data grob 'text))
(vowel-count (string-count syllable vowel-set))
(vowel-position (string-index syllable vowel-set))
(prevowel (substring syllable 0 vowel-position))
(vowel (substring syllable vowel-position (+ vowel-position 1)))
(prevowel-width (width grob prevowel))
(vowel-width (width grob vowel))
; TODO: get the note width dynamically instead of hard coded
(note-width 1.5))
(- (/ (- note-width vowel-width) 2) prevowel-width)))
chant = \relative c'' { c4 d4 c4 c4 c4 }
words = \lyricmode { aligned words do come easy }
\score {
<<
\new Voice = "melody" \chant
\new Lyrics \lyricsto "melody" \words
>>
}
\layout {
\context {
\Lyrics
\override LyricText #'X-offset = #center-on-vowel
}
}