Using any arbitrary markup as LyricHyphen

Revision as of 22:47, 26 October 2025 by Jean Abou Samra (talk | contribs) (Import snippet from LSR)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Lyric hyphens have several user-modifiable properties such as length and thickness; this doesn’t allow, however, for further tweaking, for example, rendering hyphens as rounded boxes to better match the font possibly used for LyricText, or even printing hyphens as actual glyphs rather than drawing basic boxes.

This snippet rewrites the LyricHyphen stencil procedure to use a proper glyph… or indeed any arbitrary markup.

\version "2.24.0"

%% Originally contributed by Aaron Hill:
%% https://lists.gnu.org/archive/html/lilypond-user/2019-03/msg00257.html

#(define (lyric-hyphen-text-stencil grob)
   "Draws a LyricHyphen using an arbitrary text markup."
   
   (define (span-point side common dir)
     (let ((iv
            (ly:generic-bound-extent side common)))
       (if (interval-empty? iv)
           (ly:grob-relative-coordinate side common X)
           (interval-bound iv dir))))
           
   (define (get-text-stencil grob)
     (let* ((orig (ly:grob-original grob))
            (into (ly:spanner-broken-into orig)))
       (grob-interpret-markup
        (ly:spanner-bound (if (null? into) orig (first into)) LEFT)
        (ly:grob-property grob 'text "-"))))
        
   (let* ((left-bound (ly:spanner-bound grob LEFT))
          (right-bound (ly:spanner-bound grob RIGHT))
          (common (ly:grob-common-refpoint left-bound right-bound X))
          (left-span (span-point left-bound common RIGHT))
          (right-span (span-point right-bound common LEFT))
          (span-length (- right-span left-span))
          (padding (ly:grob-property grob 'padding 0.1))
          (minimum-length (ly:grob-property grob 'minimum-length 0.3))
          (usable-length 
            (- span-length
               (if (zero? (ly:item-break-dir left-bound)) padding 0)
               (if (zero? (ly:item-break-dir right-bound)) padding 0))))
     (if (< usable-length minimum-length) '()
         (let* ((dash-sten (get-text-stencil grob))
                (dash-extent (ly:stencil-extent dash-sten X))
                (dash-length (min (interval-length dash-extent) usable-length))
                (scaled-sten (ly:stencil-scale
                              (ly:stencil-translate-axis
                               dash-sten (- (interval-start dash-extent)) X)
                              (/ dash-length (interval-length dash-extent)) 1))
                (dash-period 
                  (max (ly:grob-property grob 'dash-period 1.0) dash-length))
                (dash-count 
                  (1+ (floor (/ (- usable-length dash-length) dash-period))))
                (extra 
                  (- usable-length 
                     dash-length 
                     (* (- dash-count 1) dash-period)))
                (offset 
                  (+ (- left-span (ly:grob-relative-coordinate grob common X))
                     (if (zero? (ly:item-break-dir left-bound)) padding 0)
                     (/ extra 2))))
           (apply 
             ly:stencil-add 
             (map 
               (lambda (n)
                 (ly:stencil-translate-axis 
                   scaled-sten 
                   (+ offset (* n dash-period)) 
                   X))
               (iota dash-count)))))))

\paper {
  indent = 0
  ragged-right = ##f
  tagline = ##f
}

\relative c' {
  \omit Staff.TimeSignature
  \repeat unfold 3 {
    b4. b8 b4 b4 |
    b8 b8 b8 b8 b2 \break
  }
  r2 b'2~ \break b1~ \break b2 b2
}
\addlyrics {
  \override Score.RehearsalMark.self-alignment-X = #LEFT
  
  \mark "LyricHyphen.text is \"-\""
  \override LyricHyphen.dash-period = #5
  \override LyricHyphen.padding = #0.2
  \override LyricHyphen.stencil = #lyric-hyphen-text-stencil
  
  Lo -- rem ip -- sum do -- lor sit a -- met.
  
  \mark "LyricHyphen.text is \"–\""
  \override LyricHyphen.text = #"–" %#"\xe2\x80\x93" % U+2013
  \override LyricText.font-series = #'bold
  Lo -- rem ip -- sum do -- lor sit a -- met.
  
  \mark \markup { "LyricHyphen.text is " \circle  \with-color #red "&" }
  \override LyricHyphen.text = 
    \markup
      \circle \with-color #red 
      %% For unknown reason "\xe2\x99\xa5", which is "♥", U+2665, does not work in LSR, 
      %% replacing it with "&" 
      %% Outside of LSR it works -- Harm
      %%
      %% Outside LSR it's possible to use "♥" directly for guile-v1 and guile-v2  --Harm (May 2022)
      #"&"
  \override LyricText.font-size = #3
  Lo -- rem ip -- sum do -- lor sit a -- met.

      %% Same here for "\xe2\x89\x8b", which is "≋", U+224B, does not work in LSR, 
      %% replacing it with "~" 
      %% Outside of LSR it works -- Harm
      %%
      %% Outside LSR it's possible to use "≋" directly for guile-v1 and guile-v2  --Harm (May 2022)

  \mark \markup { "LyricHyphen.text is blue" \with-color #blue #"~" }
  \override LyricHyphen.text = 
    \markup
      \scale #'(3 . 1) \with-color #blue #"~"
  \override LyricText.font-size = #6
  Break -- ing
}