lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Snippet: Using arbitrary markup with LyricHyphen


From: Aaron Hill
Subject: Snippet: Using arbitrary markup with LyricHyphen
Date: Sat, 16 Mar 2019 05:30:35 -0700
User-agent: Roundcube Webmail/1.3.8

This whole thing started as a way to automate setting the properties of LyricHyphen based on the current LyricText font. In my use case, I am preparing music for projection, which requires larger font sizes than typical for print. The default values for things like length, thickness, and height are not suitable and require manual tweaking.

What I wanted is a way to measure the size of an actual hyphen in a font and have LyricHyphen's default stencil procedure draw a box accordingly. However, when using ly:stencil-extent against a simple glyph, the results did not reflect only the ink but also included spacing/kerning information for the glyph. Depending on the font, the effective width of a hyphen might be larger than it actually appears. The resulting LyricHyphen does not line up with nor properly mimic a real hyphen.

Now, even if I could solve the detail with glyph outlines and get the right dimensions, there is still a problem that Lyric_hyphen::print uses a rounded box with a hard-coded corner radius for its stencil. In my case, the radius was too small compared to the actual hyphen in the font I was using, so it looks too "sharp".

So, what I ended up doing was rewriting the LyricHyphen stencil procedure to use an actual hyphen glyph rather than try to draw something. And in the noble pursuit of generalization, the following approach supports *any* arbitrary markup.

%%%%
\version "2.19.82"

#(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 }
{ \omit Staff.TimeSignature
  \repeat unfold 3 { b'4. 8 4 4 | 8 8 8 8 2 \break }
  r2 b'2~ \break 1~ \break 2 2
}
\addlyrics {
  \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.
  \override LyricHyphen.text = #"\xe2\x80\x93" % U+2013
  \override LyricText.font-series = #'bold
  Lo -- rem ip -- sum do -- lor sit a -- met.
  \override LyricHyphen.text = \markup
    \circle \with-color #red #"\xe2\x99\xa5" % U+2665
  \override LyricText.font-size = #3
  Lo -- rem ip -- sum do -- lor sit a -- met.
  \override LyricHyphen.text = \markup
    \scale #'(3 . 1) \with-color #blue #"\xe2\x89\x8b" % U+224B
  \override LyricText.font-size = #6
  Break -- ing
}
%%%%

NOTE: While this procedure is largely based on Lyric_hyphen::print, it is not an exact copy. For instance, it omits the special handling of whiteout, since \whiteout is a markup command which should in theory serve an equivalent role. There is an edge case* that I could not determine how to trigger, so I skipped its handling. Lastly, I chose to compute dash count differently, as the existing logic does not seem to handle padding well and sometimes results in no hyphens being drawn when there is indeed room for some.

* This edge case is the first one in the C++ code. It provides an early out when the left bound is broken while occupying the same moment as the right bound's column. I assumed this would be the case when the LyricText after a LyricHyphen that breaks is the first moment on the next line. However, it does not appear that any LyricHyphen gets created in that scenario.


-- Aaron Hill

Attachment: lyric-hyphen-text-stencil.cropped.png
Description: PNG image

Attachment: lyric-hyphen-text-stencil.ly
Description: Text document


reply via email to

[Prev in Thread] Current Thread [Next in Thread]