Hi Jean, hi Werner,

On 05.06.23 22:28, Jean Abou Samra wrote:
Well, for my defense, I did also post a revised version of the “magnetic lyrics” snippet:

https://www.mail-archive.com/lilypond-user@gnu.org/msg149201.html

thanks for that pointer and your contributions. I reviewed that thread, since I had only noticed at the time that your, Jean’s, initial design wasn’t production-ready and not gotten around to following up…

Based on that, and on successful test on one (!) current score I was working on, I submitted the snippet to LSR as no. 1169 (pending approval) in the attached form. I hope that this is in your interest, as well as that of users who may find it there.

I also hope that LSR admins will review the ‘headers’ within the snippet code, namely the way you two are being credited and the list archive is being referred to.

Best, Simon
\version "2.24.0"

%% LSR 1169 (to be confirmed)

\include "magnetic-lyrics.ily"

%% demonstration

\paper {
  indent = 0
  line-width = 31.5
  ragged-last = ##f
}

<<
  { 2 2 }
  \addlyrics { long -- ish }
>>
% magnetic-lyrics.ily
%
%   written by
%     Jean Abou Samra <j...@abou-samra.fr>
%     Werner Lemberg <w...@gnu.org>
%
% Version 2022-Apr-15

% https://www.mail-archive.com/lilypond-user@gnu.org/msg149350.html

\version "2.23.7"

#(define (Left_hyphen_pointer_engraver context)
   "Collect syllable-hyphen-syllable occurrences in lyrics and store
them in properties.  This engraver only looks to the left.  For
example, if the lyrics input is @code{foo -- bar}, it does the
following.

@itemize @bullet
@item
Set the @code{text} property of the @code{LyricHyphen} grob between
@q{foo} and @q{bar} to @code{foo}.

@item
Set the @code{left-hyphen} property of the @code{LyricText} grob with
text @q{foo} to the @code{LyricHyphen} grob between @q{foo} and
@q{bar}.
@end itemize

Use this auxiliary engraver in combination with the
@code{lyric-@/text::@/apply-@/magnetic-@/offset!} hook."
   (let ((hyphen #f)
         (text #f))
     (make-engraver
      (acknowledgers
       ((lyric-syllable-interface engraver grob source-engraver)
        (set! text grob)))
      (end-acknowledgers
       ((lyric-hyphen-interface engraver grob source-engraver)
        (when (not (grob::has-interface grob 'lyric-space-interface))
          (set! hyphen grob))))
      ((stop-translation-timestep engraver)
       (when (and text hyphen)
         (ly:grob-set-object! text 'left-hyphen hyphen))
       (set! text #f)
       (set! hyphen #f)))))

#(define (lyric-text::apply-magnetic-offset! grob)
   "If the space between two syllables is less than the value in
property @code{LyricText@/.details@/.squash-threshold}, move the right
syllable to the left so that it gets concatenated with the left
syllable.

Use this function as a hook for
@code{LyricText@/.after-@/line-@/breaking} if the
@code{Left_@/hyphen_@/pointer_@/engraver} is active."
   (let ((hyphen (ly:grob-object grob 'left-hyphen #f)))
     (when hyphen
       (let ((left-text (ly:spanner-bound hyphen LEFT)))
         (when (grob::has-interface left-text 'lyric-syllable-interface)
           (let* ((common (ly:grob-common-refpoint grob left-text X))
                  (this-x-ext (ly:grob-extent grob common X))
                  (left-x-ext
                   (begin
                     ;; Trigger magnetism for left-text.
                     (ly:grob-property left-text 'after-line-breaking)
                     (ly:grob-extent left-text common X)))
                  ;; `delta` is the gap width between two syllables.
                  (delta (- (interval-start this-x-ext)
                            (interval-end left-x-ext)))
                  (details (ly:grob-property grob 'details))
                  (threshold (assoc-get 'squash-threshold details 0.2)))
             (when (< delta threshold)
               (let* (;; We have to manipulate the input text so that
                      ;; ligatures crossing syllable boundaries are not
                      ;; disabled.  For languages based on the Latin
                      ;; script this is essentially a beautification.
                      ;; However, for non-Western scripts it can be a
                      ;; necessity.
                      (lt (ly:grob-property left-text 'text))
                      (rt (ly:grob-property grob 'text))
                      ;; Append new syllable.
                      (ltrt (if (and (string? lt) (string? rt))
                                (string-append lt rt)
                                (make-concat-markup (list lt rt))))
                      ;; Right-align `ltrt` to the right side.
                      (markup (grob-interpret-markup
                               grob
                               (make-translate-markup
                                (cons (interval-length this-x-ext) 0)
                                (make-right-align-markup ltrt)))))
                 (begin
                   ;; Don't print `left-text`.
                   (ly:grob-set-property! left-text 'stencil #f)
                   ;; Set text and stencil (which holds all collected
                   ;; syllables so far) and shift it to the left.
                   (ly:grob-set-property! grob 'text ltrt)
                   (ly:grob-set-property! grob 'stencil markup)
                   (ly:grob-translate-axis! grob (- delta) X))))))))))


#(define (lyric-hyphen::displace-bounds-first grob)
   ;; Make very sure this callback isn't triggered too early.
   (let ((left (ly:spanner-bound grob LEFT))
         (right (ly:spanner-bound grob RIGHT)))
     (ly:grob-property left 'after-line-breaking)
     (ly:grob-property right 'after-line-breaking)
     (ly:lyric-hyphen::print grob)))


\layout {
  \context {
    \Lyrics
    \consists #Left_hyphen_pointer_engraver
    \override LyricText.after-line-breaking =
#lyric-text::apply-magnetic-offset!
    \override LyricHyphen.stencil = #lyric-hyphen::displace-bounds-first
    \override LyricText.details.squash-threshold = 0.4
    \override LyricHyphen.minimum-distance = 0
    \override LyricHyphen.minimum-length = 0.4
    \override LyricSpace.minimum-distance = 1
  }
}

Reply via email to