Re: bug in magnetic snapping lyrics engraver
> Here is an example where it fails to position lyrics correctly (see > last system in the image). And while we are at it: Here is another bug, already reported in https://lists.gnu.org/archive/html/lilypond-user/2020-03/msg00289.html I've slightly sharpened the test to exclude the possibility of kerning being the cause of the (buggy) result. Werner \version "2.20.0" \include "magnetic-lyrics.ily" \new Lyrics \lyricmode { "|foobar|" "|foobar|" \break foo -- "|bar|" "|foobar|" } \layout { \context { \Lyrics \override LyricWord.after-line-breaking = #(lyric-word-compressor 0.5) } \context { \Score \remove "Bar_number_engraver" } } \paper { indent = 0 ragged-right = ##t system-system-spacing.basic-distance = 0 system-system-spacing.minimum-distance = 0 system-system-spacing.padding = 0 }
Re: bug in magnetic snapping lyrics engraver
Le 06/04/2022 à 14:33, Werner LEMBERG a écrit : Is someone taking care of bugs in the magnetic snapping lyrics engraver? Here is an example where it fails to position lyrics correctly (see last system in the image). Werner PS: I've attached a version of `magnetic-lyrics.ily` that actually works with the current development version (using an updated `add-grob-definition` routine). To be honest, I don't know why the snippet is so complicated. I'd just have done: \version "2.23.7" #(define (Left_hyphen_pointer_engraver context) (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) (let ((hyphen (ly:grob-object grob 'left-hyphen #f))) (when hyphen (let* ((left-text (ly:spanner-bound hyphen LEFT)) (common (ly:grob-common-refpoint grob left-text X)) (my-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 (- (interval-start my-x-ext) (interval-end left-x-ext))) (details (ly:grob-property grob 'details)) (threshold (assoc-get 'squash-threshold details 0.2))) (when (< delta threshold) (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.7 \override LyricHyphen.minimum-distance = 0 \override LyricHyphen.minimum-length = 0 \override LyricSpace.minimum-distance = 1 } } << \new Voice = "foo" \relative c' { \repeat unfold 16 { a8 b a2 a8 b } } \new Lyrics \lyricsto "foo" { \repeat unfold 10 { foo } \repeat unfold 10 { foo -- \markup \caps bar } \repeat unfold 10 { \markup \bold syl -- la -- ble } a \markup \with-color #red ran -- \markup \box dom string of mo -- no -- syl -- la -- bic and mul -- ti -- \markup \fontsize #5 syl -- la -- bic \markup \bold \underline ver -- \markup \italic bi -- age \markup { \stencil #(make-circle-stencil 0.5 0 #f) } } Which seems to work, and doesn't have your first bug. Did I miss something? The second bug is super hard to fix: when you tell LilyPond about spacing constraints between lyrics, before line breaking, you need to assume a lyric syllable won't be moved to the left by 'magnetism', because if you do, the spacer might think it can space its right neighbor more to the left due to the space left by moving the syllable to the left, and if the compression cannot actually be done, it will collide. But when the compression does get done, and the room is left empty, horizontal spacing has already been done and it is too late to tell the right neighbor that it would have room on its left. Am I making sense? Jean
Re: bug in magnetic snapping lyrics engraver
Hi Jean, > To be honest, I don't know why the snippet is so complicated. Perhaps a corollary to Clarke's Law? “Every sufficiently primitive technology is indistinguishable from garbage.” ;) [No offence to the wonderful coders who wrote the existing snippet! Just a funny observation re: Clarke.] > I'd just have done: I was the main squeaky wheel for whom this grease was originally created. I'll test out your version and see if it solves all the use-cases I had/have. Cheers, Kieren.
Re: bug in magnetic snapping lyrics engraver
Le 06/04/2022 à 20:17, Jean Abou Samra a écrit : Did I miss something? Whoops, big problem at line breaks ... \version "2.23.7" #(define (Left_hyphen_pointer_engraver context) (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) (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)) (my-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 (- (interval-start my-x-ext) (interval-end left-x-ext))) (details (ly:grob-property grob 'details)) (threshold (assoc-get 'squash-threshold details 0.2))) (when (< delta threshold) (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 = 10 \override LyricHyphen.minimum-distance = 0 \override LyricHyphen.minimum-length = 0.2 \override LyricSpace.minimum-distance = 1 } }
Re: bug in magnetic snapping lyrics engraver
> The second bug is super hard to fix: when you tell LilyPond about > spacing constraints between lyrics, before line breaking, you need > to assume a lyric syllable won't be moved to the left by > 'magnetism', because if you do, the spacer might think it can space > its right neighbor more to the left due to the space left by moving > the syllable to the left, and if the compression cannot actually be > done, it will collide. But when the compression does get done, and > the room is left empty, horizontal spacing has already been done and > it is too late to tell the right neighbor that it would have room on > its left. Am I making sense? This makes sense, thanks! Werner
Re: bug in magnetic snapping lyrics engraver
>> Did I miss something? > > Whoops, big problem at line breaks ... This is ingenious :-) Thanks *a lot* for this solution. In my tests it seems to work fine. Two comments. * I *strongly* vote for polishing and documenting this so that it can be added to LilyPond. It's an invaluable feature IMHO, greatly enhancing Lyrics. * You have redefined the `LyricHyphen` stencil. AFAICS, this change could be directly integrated into `ly:lyric-hyphen::print`, right? Doing so would allow users to change the stencil by themselves if necessary. Otherwise I vote for introducing a property `hyphen-formatter` for this job. Werner
Re: bug in magnetic snapping lyrics engraver
Le 07/04/2022 à 11:44, Werner LEMBERG a écrit : Did I miss something? Whoops, big problem at line breaks ... This is ingenious :-) Thanks *a lot* for this solution. In my tests it seems to work fine You're welcome. Two comments. * I *strongly* vote for polishing and documenting this so that it can be added to LilyPond. It's an invaluable feature IMHO, greatly enhancing Lyrics. Well, I'm afraid you can't 'vote' for someone else doing volunteer work :-) Right now, I'm struggling to get my work on break substitution out, which has been brewing for four months. After that ... I have a list of LilyPond TODO items. Currently the length of that list is 24 :-) So I can enqueue it on the list, but don't expect me to work on it anytime soon. On the other hand, by all means feel free to work yourself on polishing and integrating it :-) * You have redefined the `LyricHyphen` stencil. AFAICS, this change could be directly integrated into `ly:lyric-hyphen::print`, right? Doing so would allow users to change the stencil by themselves if necessary. Otherwise I vote for introducing a property `hyphen-formatter` for this job. Yes, it can. That said, it remains sort of a crutch. Although there are not many solutions in current LilyPond (and my approach is not an outlier in the code base either), I'm not very happy with this use of after-line-breaking. Without trying right now, I think I can easily find edge cases involving ly:side-position-interface::move-to-extremal-staff (with bar numbers / rehearsal marks / tempo marks / volta brackets) that would break the snippet without this stencil override. Having it means catering for edge cases in drawing the stencil at least, but I have not investigated whether there are currently any X-extent callbacks that could manage to trigger outside-staff positioning for example, which could make for odd spacing. In short: I think it should work OK in non-esoteric cases, although I would have to look more closely to be sure. Jean
Re: bug in magnetic snapping lyrics engraver
Hi Werner (et al.), > This is ingenious :-) Thanks *a lot* for this solution. +1 (x2) > * I *strongly* vote for polishing and documenting this so that it can > be added to LilyPond. It's an invaluable feature IMHO, greatly > enhancing Lyrics. YES PLEASE!! If anyone is interested in discussing one potentially related request — also having to do with spacing — I'd appreciate it. It may not have any impact on this solution, but my instinct is it might, so I wanted to throw it out there. Consider the following snippet: %%% SNIPPET BEGINS \version "2.23.4" \layout { indent = 0 } music = { \tuplet 3/2 { c'8 8 8 } } words = \lyricmode { \tag #'fix { \once \override LyricText.self-alignment-X = #0.25 } Loong! Not long. } \score { << \new Staff \music \addlyrics \words >> \layout { ragged-right = ##f line-width = 2.5\in } } \score { << \new Staff \removeWithTag #'fix { \music } \addlyrics \words >> } \score { << \new Staff \keepWithTag #'fix { \music } \addlyrics \words >> \layout { ragged-right = ##f line-width = 2\in } } %%% SNIPPET ENDS A *large* percentage of my tweaking time is spent adjusting the alignment of lyrics so that the note spacing is as natural as possible: aligning the first syllable of a triplet more to the right and the last syllable more to the left, etc. I would love to be able to identify a block of notes (anything from a pair to a whole measure or more!) so that the spacing engine would privilege the note spacing in that group over the spacing of the lyrics under it, compressing or expanding LyricWords [hence the intersection with this feature!], changing self-alignment-X or X-offset, etc., in order to keep the notes the most natural. Janek started to work on this as part of the GSoC Lyric work (10 years ago, maybe?!?!), but never finished. It's obviously a non-trivial feature to implement… but I wanted to bring it up just in case the potential implementation of it in the future might change the way that LyricWord is implemented right now. Thoughts? Kieren
Re: bug in magnetic snapping lyrics engraver
On Thu, Apr 7, 2022 at 10:04 AM Kieren MacMillan < kie...@kierenmacmillan.info> wrote: > Hi Werner (et al.), > > > This is ingenious :-) Thanks *a lot* for this solution. > > +1 (x2) > > > * I *strongly* vote for polishing and documenting this so that it can > > be added to LilyPond. It's an invaluable feature IMHO, greatly > > enhancing Lyrics. > > YES PLEASE!! > > Kieren, this might be a place for you to make your first commit, rather than trying to get Charles's work in. This is a much smaller piece of code to polish and document. Of course, I can't decide for you, but I thought it might be worth your consideration. Thanks, Carl
Re: bug in magnetic snapping lyrics engraver
>> This is ingenious :-) Thanks *a lot* for this solution. In my >> tests it seems to work fine > > You're welcome. I spoke too soon :-) There is a problem with ligatures at the syllable boundaries (and kerning). While this can be considered an ugliness in languages based on the latin script, it might be a real showstopper for non-latin scripts like Devanagari. In other words, it is not sufficient to shift the right syllable in a syllable pair to the left. Instead, the following should be done. * Concatenate the text/markup of the left syllable with the right text/markup *on the input level* (for example, by using `make-concat-markup`) so that Pango/Cairo can handle all affected syllables together. * Store the result in the right syllable and adjust the horizontal position. This allows accumulation for multi-syllable words. * Set the text/markup in the left syllable to empty. I tried to implement this, but I failed. In this replacement code ``` (when (< delta threshold) (let ((lt (ly:grob-property left-text 'text)) (rt (ly:grob-property grob 'text))) (begin (ly:grob-set-property! left-text 'text empty-markup) (ly:grob-set-property! grob 'text (make-concat-markup (list lt rt))) ``` the `ly:grob-set-property!` calls have zero effect. I guess that at this point of time it is too late for modifying properties, and the grobs have already been converted to stencils. Maybe this is the reason for the more complicated approach in David's original approach? Do you know a quick fix for your code? Werner
Re: bug in magnetic snapping lyrics engraver
> There is a problem with ligatures at the syllable boundaries (and > kerning). [...] it is not sufficient to shift the right syllable > in a syllable pair to the left. Instead, the following should be > done. [...] Meanwhile I could implement this :-) I will submit a MR soon. Werner
Re: bug in magnetic snapping lyrics engraver
Le 14/04/2022 à 17:39, Werner LEMBERG a écrit : There is a problem with ligatures at the syllable boundaries (and kerning). [...] it is not sufficient to shift the right syllable in a syllable pair to the left. Instead, the following should be done. [...] Meanwhile I could implement this :-) I will submit a MR soon. How did you do that? Sorry to rain on your parade, but I would not want you to put a lot of work in it if it will not be of mergeable quality. Jean
Re: bug in magnetic snapping lyrics engraver
> How did you do that? Sorry to rain on your parade, but I would not > want you to put a lot of work in it if it will not be of mergeable > quality. Attached. In the end the necessary modifications were surprisingly minor. Please comment, there is certainly room for improvements. Werner diff --git a/lily/lyric-hyphen.cc b/lily/lyric-hyphen.cc index 23cd76bd94..897de7d7f7 100644 --- a/lily/lyric-hyphen.cc +++ b/lily/lyric-hyphen.cc @@ -48,6 +48,10 @@ Lyric_hyphen::print (SCM smob) && !from_scm (get_property (me, "after-line-breaking" return SCM_EOL; + // Ensure that bounds are displaced first. + (void) get_property (bounds[LEFT], "after-line-breaking"); + (void) get_property (bounds[RIGHT], "after-line-breaking"); + Grob *common = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS); Interval span_points; diff --git a/scm/scheme-engravers.scm b/scm/scheme-engravers.scm index 07e3be3691..5131beef1f 100644 --- a/scm/scheme-engravers.scm +++ b/scm/scheme-engravers.scm @@ -943,6 +943,101 @@ Engraver to print a line between two @code{Fingering} grobs."))) (description . "Create repeat counts within lyrics for modern transcriptions of Gregorian chant."))) +(define (Left_hyphen_pointer_engraver context) + (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-public (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)) + +(ly:register-translator + Left_hyphen_pointer_engraver 'Left_hyphen_pointer_engraver + '((grobs-created . ()) + (events-accepted . ()) + (properties-read . ()) + (properties-written . ()) + (description . "\ +Collect syllable-hyphen-syllable occurrences in lyrics and store them
Re: bug in magnetic snapping lyrics engraver
Hi all, On 4/6/22 20:17, Jean Abou Samra wrote: > Le 06/04/2022 à 14:33, Werner LEMBERG a écrit : >> Is someone taking care of bugs in the magnetic snapping lyrics >> engraver? Here is an example where it fails to position lyrics >> correctly (see last system in the image). >> >> >> Werner >> >> >> PS: I've attached a version of `magnetic-lyrics.ily` that actually >> works with the current development version (using an updated >> `add-grob-definition` routine). > > > > To be honest, I don't know why the snippet is so complicated. > I'd just have done: One of the reasons might be that all participants have been 8 years younger and less experienced back then, and *some* later touches (at least mine) have been made by the trusted techniques of shotgun debugging and interpreter error-driven (rather than test-driven) development. And folks like Harm only had so much chance to tidy up my mess. ;-) My literally last exposure to Lilypond was meeting you folks in Jan 2020 in Salzburg, and I'll need to dig out my old tests, but I'll give it a shot; having this integrated would be such an amazing improvement. When it comes to LyricHyphen handling, some pieces in this thread might be relevant as well: https://mail.gnu.org/archive/html/lilypond-user/2016-12/msg00361.html Unfortunately IMHO, Knut's work on automatic LyricExtenders has never reached its prime time... Cheers, Alex smime.p7s Description: S/MIME Cryptographic Signature
Re: bug in magnetic snapping lyrics engraver
Le 14/04/2022 à 18:18, Werner LEMBERG a écrit : How did you do that? Sorry to rain on your parade, but I would not want you to put a lot of work in it if it will not be of mergeable quality. Attached. In the end the necessary modifications were surprisingly minor. Please comment, there is certainly room for improvements. Thanks. This is a bit what I feared: in this state, there is potential for things going wrong with other after-line-breaking callbacks. Those are the kind of thing that can be buggy in obscure circumstances. I'm a little nervous on having my "off-the-top" / "first thing that seemed to work" solution in the code base :-) OTOH, it's *probably* not too difficult to refactor this to use a separate 'original-text property and a 'text property computed by a callback, with X-offset computed only after-line-breaking, X-extent left empty and springs-and-rods doing the magic that would otherwise be done by Separation_item::set_distance, at the condition of letting Grob::extent skip the X-offset calculation in case the extent is found to be empty (which could be a good idea regardless actually). Or something like that. Too bad that we don't have pure widths (we could if I finally managed to finish a big patch that has been pending for about a year), but it should be possible to do without I think. I think. But right now, I'm a bit swamped due to a somewhat important exam next Thursday. [Alex; glad to meet you BTW] My literally last exposure to Lilypond was meeting you folks in Jan 2020 in Salzburg, Well, not me ;-) (I knew little about LilyPond at that time and had not started to contribute.) and I'll need to dig out my old tests, but I'll give it a shot; having this integrated would be such an amazing improvement. If you guys are ready to clean up this thing, that would be a relief for me given the amount of other contributions that I am trying to get around to. I know I was a bit elliptic above (too tired ATM), but I am happy to provide guidance and discuss solutions. Jean
Re: bug in magnetic snapping lyrics engraver
>> Attached. In the end the necessary modifications were surprisingly >> minor. Please comment, there is certainly room for improvements. > > Thanks. This is a bit what I feared: in this state, there is > potential for things going wrong with other after-line-breaking > callbacks. Those are the kind of thing that can be buggy in obscure > circumstances. I'm a little nervous on having my "off-the-top" / > "first thing that seemed to work" solution in the code base :-) I think your code is very clear and easy to understand. My extensions aren't complicated either. It also works nicely and flawless in combination with the new `VaticanaLyrics` context. So please be more specific about your fears. You told us that you are working on the breaking algorithm since a few months, so I guess you are the best person to voice objections and give counterexamples. Kieren, can you test this, too? We should probably continue the discussion on lilypond-devel, since it seems to get bloody technical. :-) > OTOH, it's *probably* not too difficult to refactor this to use a > separate 'original-text property and a 'text property computed by a > callback, with X-offset computed only after-line-breaking, X-extent > left empty and springs-and-rods doing the magic that would otherwise > be done by Separation_item::set_distance, at the condition of > letting Grob::extent skip the X-offset calculation in case the > extent is found to be empty (which could be a good idea regardless > actually). This is way too complicated for me, sorry. > Or something like that. Too bad that we don't have pure widths (we > could if I finally managed to finish a big patch that has been > pending for about a year), but it should be possible to do without I > think. I think. Well, I fear I can't help you with the coding if you think this is the best route to go. But as usual: The better is the enemy of the good :-) > But right now, I'm a bit swamped due to a somewhat important exam > next Thursday. I wish you success! > If you guys are ready to clean up this thing, that would be a relief > for me given the amount of other contributions that I am trying to > get around to. Define 'clean up'. AFAICS, what you have outlined above is not a cleanup but a complete reimplementation based on a different algorithm... Werner
Re: bug in magnetic snapping lyrics engraver
>> How did you do that? Sorry to rain on your parade, but I would not >> want you to put a lot of work in it if it will not be of mergeable >> quality. > > Attached. In the end the necessary modifications were surprisingly > minor. Please comment, there is certainly room for improvements. Attached is another version for testing without the need to patch LilyPond. Werner % magnetic-lyrics.ily % % written by % Jean Abou Samra % Werner Lemberg % % Version 2022-Apr-15 \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_h