Hi Alexander, hi everybody!

And we might need to offer a way to remove a LyricExtender event. Unless we go 
the radical route and ...

After a bit of thinking I'd say: go the radical route.

Attached is a patch against current master that implements it that way. An 
additional no-extender property
is added that can only be overridden by the force-extender property.

 If a user doesn't want it, we need to provide a function \extenderOff (that 
translates, e.g., to \override LyricExtender.stencil = ##f).

\layout {
    \Lyrics
    \override LyricExtender.no-extender = ##t
}

Only thing to consider there: We'd need a "stop sign" for extenders that shall 
only extend to some point in a _ _ _ sequence, as required in a divisi lyrics setting.

"" and \markup\null are usefull, see 5th exampled in attached lyrextest.ly/pdf

I know that the documentation would need some work, but there is no reason to 
start with that until
it is known that the changed code would be accepted.

Please test ...

cu,
 Knut
>From 53e80c9c17cfa2b11deb15ccfef4587b82c06d52 Mon Sep 17 00:00:00 2001
From: Knut Petersen <knut_peter...@t-online.de>
Date: Thu, 15 Dec 2016 12:52:06 +0100
Subject: [PATCH] Automated lyric extenders

Automatically add lyric extenders whenever they are
appropriate.

Add new property no-extender to forbid all extenders
unless overridden by the 2nd new boolean property
force-extender.

Enabled by default.

Signed-off-by: Knut Petersen <knut_peter...@t-online.de>
---
 lily/lyric-extender.cc            | 44 +++++++++++++++++++++------------------
 scm/define-context-properties.scm |  2 ++
 scm/define-grob-properties.scm    |  6 ++++++
 scm/define-grobs.scm              |  2 ++
 scm/music-functions.scm           | 12 ++++++++---
 5 files changed, 43 insertions(+), 23 deletions(-)

diff --git a/lily/lyric-extender.cc b/lily/lyric-extender.cc
index 8afe2c5569..f7d49ac916 100644
--- a/lily/lyric-extender.cc
+++ b/lily/lyric-extender.cc
@@ -45,51 +45,53 @@ Lyric_extender::print (SCM smob)
   common = common->common_refpoint (me->get_system (), X_AXIS);
 
   Real sl = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
+  bool at_start_of_line = !left_edge->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface"));
+  bool at_end_of_line = me->get_bound (RIGHT)->break_status_dir ();
+  bool force_extender = to_boolean (me->get_property ("force-extender"));
+  bool no_extender = to_boolean (me->get_property ("no-extender"));
+
+  if (!force_extender && no_extender)
+    return SCM_EOL;
 
   extract_grob_set (me, "heads", heads);
 
-  if (!heads.size ())
+  if (!heads.size () || (!force_extender && !at_start_of_line && !at_end_of_line && heads.size () < 2))
     return SCM_EOL;
 
   common = common_refpoint_of_array (heads, common, X_AXIS);
 
   Real left_point = 0.0;
-  if (left_edge->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface")))
+  if (!at_start_of_line)
     left_point = left_edge->extent (common, X_AXIS)[RIGHT];
-  else if (heads.size ())
-    left_point = heads[0]->extent (common, X_AXIS)[LEFT];
   else
-    left_point = left_edge->extent (common, X_AXIS)[RIGHT];
+    left_point = heads[0]->extent (common, X_AXIS)[LEFT];
 
   if (isinf (left_point))
     return SCM_EOL;
 
-  /* It seems that short extenders are even lengthened to go past the
-     note head, but haven't found a pattern in it yet. --hwn 1/1/04  */
-  SCM minlen = me->get_property ("minimum-length");
-  Real right_point
-    = left_point + (robust_scm2double (minlen, 0));
-
-  right_point = min (right_point, me->get_system ()->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
-
-  if (heads.size ())
-    right_point = max (right_point, heads.back ()->extent (common, X_AXIS)[RIGHT]);
-
   Real h = sl * robust_scm2double (me->get_property ("thickness"), 0);
   Drul_array<Real> paddings (robust_scm2double (me->get_property ("left-padding"), h),
                              robust_scm2double (me->get_property ("right-padding"), h));
 
+  Real minlen = robust_scm2double (me->get_property ("minimum-length"), 0);
+
+  Real right_point = heads.back ()->extent (common, X_AXIS)[RIGHT];
+
+  if (force_extender)
+    right_point = max (heads.back ()->extent (common, X_AXIS)[RIGHT], left_point + minlen);
+
   if (right_text)
     right_point = min (right_point, (robust_relative_extent (right_text, common, X_AXIS)[LEFT] - paddings[RIGHT]));
 
-  /* run to end of line. */
-  if (me->get_bound (RIGHT)->break_status_dir ())
+  if (at_end_of_line)
     right_point = max (right_point, (robust_relative_extent (me->get_bound (RIGHT), common, X_AXIS)[LEFT] - paddings[RIGHT]));
 
-  left_point += paddings[LEFT];
+  if (!at_start_of_line)
+    left_point += paddings[LEFT];
+
   Real w = right_point - left_point;
 
-  if (w < 1.5 * h)
+  if (w < minlen && !at_start_of_line && !at_end_of_line && !force_extender)
     return SCM_EOL;
 
   Stencil mol (Lookup::round_filled_box (Box (Interval (0, w),
@@ -111,4 +113,6 @@ ADD_INTERFACE (Lyric_extender,
                "next "
                "right-padding "
                "thickness "
+               "force-extender "
+               "no-extender "
               );
diff --git a/scm/define-context-properties.scm b/scm/define-context-properties.scm
index 75f1807e61..7a4024c167 100644
--- a/scm/define-context-properties.scm
+++ b/scm/define-context-properties.scm
@@ -331,6 +331,7 @@ staff switches by a thin line.")
      (fontSize ,number? "The relative size of all grobs in a context.")
      (forbidBreak ,boolean? "If set to @code{#t}, prevent a line break
 at this point.")
+     (force-extender ,boolean? "Whether to force lyric extenders.")
      (forceClef ,boolean? "Show clef symbol, even if it has not
 changed.  Only active for the first clef after the property is set, not
 for the full staff.")
@@ -483,6 +484,7 @@ for a minor chord")
 
      (noChordSymbol ,markup? "Markup to be displayed for rests in a
 ChordNames context.")
+     (no-extender ,boolean? "Whether to inhibit lyric extenders.")
      (noteToFretFunction ,procedure? "Convert list of notes and list of
 defined strings to full list of strings and fret numbers.
 Parameters: The context, a list of note events, a list of
diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm
index c234792dd2..0e8cc5d576 100644
--- a/scm/define-grob-properties.scm
+++ b/scm/define-grob-properties.scm
@@ -330,6 +330,10 @@ allowed.")
      (footnote ,boolean? "Should this be a footnote or in-note?")
      (footnote-music ,ly:music? "Music creating a footnote.")
      (footnote-text ,markup? "A footnote for the grob.")
+     (force-extender ,boolean? "Force a lyric extender to be generated
+if none would be generated otherwise and/or force it to be at least as
+wide as indicated by property @code{minimum-width} unless it would
+collide with the next syllable. Overrides property @code{no-extender}.")
      (force-hshift ,number? "This specifies a manual shift for notes
 in collisions.  The unit is the note head width of the first voice
 note.  This is used by @rinternals{note-collision-interface}.")
@@ -663,6 +667,8 @@ syllable following an extender).")
      (no-alignment ,boolean? "If set, don't place this grob in a
 @code{VerticalAlignment}; rather, place it using its own
 @code{Y-offset} callback.")
+     (no-extender ,boolean? "Inhibits generation of lyric extenders.
+Can be overridden by property @code{force-extender}.")
      (no-ledgers ,boolean? "If set, don't draw ledger lines on this
 object.")
      (no-stem-extend ,boolean? "If set, notes with ledger lines do not
diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm
index e36ea12bf2..d4ad46e644 100644
--- a/scm/define-grobs.scm
+++ b/scm/define-grobs.scm
@@ -1361,7 +1361,9 @@
 
     (LyricExtender
      . (
+        (force-extender . #f)
         (minimum-length . 1.5)
+        (no-extender . #f)
         (stencil . ,ly:lyric-extender::print)
         (thickness . 0.8) ; line-thickness
         (Y-extent . (0 . 0))
diff --git a/scm/music-functions.scm b/scm/music-functions.scm
index b1dc2f9c61..8cb9cf7ac5 100644
--- a/scm/music-functions.scm
+++ b/scm/music-functions.scm
@@ -728,10 +728,16 @@ making it possible to @code{\\revert} to any previous value afterwards."
          'articulation-type name
          properties))
 
+(define (add-extender! event)
+   (ly:music-set-property! event 'articulations
+     (append (ly:music-property event 'articulations)
+             (list (make-music (quote ExtenderEvent)))))
+   event)
+
 (define-public (make-lyric-event string duration)
-  (make-music 'LyricEvent
-              'duration duration
-              'text string))
+  (if (and (string? string)(string=? " " string))
+      (make-music 'LyricEvent 'duration duration 'text string)
+      (add-extender! (make-music 'LyricEvent 'duration duration 'text string))))
 
 (define-safe-public (make-span-event type span-dir)
   (make-music type
-- 
2.11.0

\version "2.19.53"

\paper { ragged-right = ##f }

\pointAndClickOff

#(set-global-staff-size 16)

\markup { "automatic extenders, minimum-length 8 "} 
<<
    { c''1 2 ~ 2 2 4 ~ 4 4 8 ~ 8 8  16 ~ 16 4 1  \bar "|." }
    \addlyrics { \override Lyrics.LyricExtender.minimum-length = #8
                 \repeat unfold 4 {  foo -- bar }}
>>

\markup { "automatic extenders, minimum-length 4 "} 
<<
    { c''1 2 ~ 2 2 4 ~ 4 4 8 ~ 8 8  16 ~ 16 4 1  \bar "|." }
    \addlyrics { \override Lyrics.LyricExtender.minimum-length = #4
                 \repeat unfold 4 {  foo -- bar }}
>>

\markup { "automatic extenders, minimum-length 1 "} 
<<
    { c''1 2 ~ 2 2 4 ~ 4 4 8 ~ 8 8  16 ~ 16 4 1  \bar "|." }
    \addlyrics { \override Lyrics.LyricExtender.minimum-length = #1
                 \repeat unfold 4 {  foo -- bar }}
>>

feo = {\once \override Lyrics.LyricExtender.force-extender = ##t}

\markup { "automatic extenders, mixed manual and automatic melismata, extender on last note forced "} 
<<
    { \autoBeamOff
      c''2 2 4\( 4 4 4\)
      4 4 4\( 4( 4) 8[ 8] 8\)
      16\(\melisma 16\melismaEnd 4\) 1
      \bar "|." }
    \addlyrics {
        \override Lyrics.LyricExtender.minimum-length = #8
        foo -- _ bar _ _ _ foo -- _ bar _ _ _ 
        foo -- _ \feo bar _ _ _ }
>>

\markup { "automatic extenders, mixed manual and automatic melismata, first extender shortened, extender on last note forced "} 
<<
    { \autoBeamOff
      c''2 2 4\( 4 4 4\)
      4 4 4\( 4( 4) 8[ 8] 8\)
      16\(\melisma 16\melismaEnd 4\) 1
      \bar "|." }
    \addlyrics {
        \override Lyrics.LyricExtender.minimum-length = #8
        foo -- _ \feo bar _ "" _  foo -- _ bar _ _ _ 
        foo -- _ \feo bar _ _ _ }
>>

\markup { "Issue 1006 revisited, last extender forced and tweaked to the left, minimum-length 2 "} 
\score { 
  <<
    \new Staff \relative c'' { 
      \time 2/1
      \repeat volta 2 {
        r4 g4 d'2. a4 bes2~
        bes  a2 g1~
      }
      \alternative{
       { g2 fis4 e fis1 d'1 c2 bes}
       { g\repeatTie fis4 e fis1 }
      }
      \bar "|."
    }
    \addlyrics {
       \override Lyrics.LyricExtender.minimum-length = #2
        of Prin -- ces all _ _ the _ flow’r. Who took a-
       \feo \tweak self-alignment-X #-4 \markup\null the _ flow’r.
    }
  >>
}

\markup { "Issue 1006 revisited, last extender forced and tweaked to the left, minimum-length 1 "} 
\score { 
  <<
    \new Staff \relative c'' { 
      \time 2/1
      \repeat volta 2 {
        r4 g4 d'2. a4 bes2~
        bes  a2 g1~
      }
      \alternative{
       { g2 fis4 e fis1 d'1 c2 bes}
       { g\repeatTie fis4 e fis1 }
      }
      \bar "|."
    }
    \addlyrics {
       \override Lyrics.LyricExtender.minimum-length = #1
        of Prin -- ces all _ _ the _ flow’r. Who took a-
       \feo \tweak self-alignment-X #-4 \markup\null the _ flow’r.
    }
  >>
}

\layout {
    \Lyrics
    \override LyricExtender.no-extender = ##f % Use ##t to disable all but forced extenders
}

Attachment: lyrextest.pdf
Description: Adobe PDF document

_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to