[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
lyric-hyphen-text-stencil.cropped.png
Description: PNG image
lyric-hyphen-text-stencil.ly
Description: Text document
- Snippet: Using arbitrary markup with LyricHyphen,
Aaron Hill <=