/* GENERAL INFORMATION */
/**********************************************************/
-/* It has been requested that each web manual be styled using a
- different color. To faciliate that, each manual is being
- assigned a hue value on the HSB color chart. All specific
- colors for a manual will be shades and tints of that hue.
+/* Each web manual is styled using a different color. Each
+ one has been assigned a hue value on the HSB color chart.
+ All specific colors for a manual are shades and tints
+ of that hue.
+
+ The brown of the usage manual does not follow this scheme.
+ Neither does the 'default' olive green of the glossary,
+ essay, snippets, and changes manuals.
Manual Color Hue
learning green 120
essay
notation blue 205
- usage yellow 50
+ usage brown
snippets
changes
height: 100%;
font-size: 100%;
line-height: 1.125;
- color: #000;
+ color: #222;
background-color: #fff;
}
}
.settitle {
- background: #657f40;
- color: white;
font-size: 2em;
text-align: center;
- padding: 0.4em 0.5em;
- border: solid black;
- border-width: 1px 0;
- margin: 0 0 10px 0;
+ padding: 0.8em 0.5em;
+ margin: 0;
}
-body.learning .settitle { background-color: #407f40; }
-body.notation .settitle { background-color: #40657f; }
-body.usage .settitle { background-color: #7f7f33; }
-body.extending .settitle { background-color: #7f4040; }
-body.internals .settitle { background-color: #6a407f; }
-body.contributor .settitle { background-color: #000000; }
-
.chapter, .appendix, .unnumbered,
.section, .appendixsec, .unnumberedsec,
.subsection, .appendixsubsec, .unnumberedsubsec,
/* BLOCK FORMATTING */
/***********************************************************/
-blockquote, .smallexample {
+blockquote,
+.smallexample {
width: 96%;
padding: 0;
- border: solid #b1d281;
- border-width: 1px 1px 1px 5px;
+ border: solid #d5d7d6;
+ border-width: 1px 1px 1px 3px;
+ border-radius: 5px;
margin: 1em auto;
background-color: white;
}
-body.learning blockquote, body.learning .smallexample { border-color: #00ff00; }
-body.notation blockquote, body.notation .smallexample { border-color: #0095ff; }
-body.usage blockquote, body.usage .smallexample { border-color: #ffff00; }
-body.extending blockquote, body.extending .smallexample { border-color: #ff0000; }
-body.internals blockquote, body.internals .smallexample { border-color: #aa00ff; }
-body.contributor blockquote, body.contributor .smallexample { border-color: #000000; }
-
blockquote p, pre.smallexample {
padding: 1em;
margin: 0;
}
table.cartouche {
- background: #f5f5dc;
+ background: #eef;
width: 85%;
border-collapse: collapse;
padding: 0 0.5em;
- border: 2px solid #8f5902;
+ border: 1px solid #c8c8c8;
margin: 0 auto 1em;
}
-body.learning table.cartouche { background-color: #cfe5cf; border: 2px solid #7db27d; }
-body.notation table.cartouche { background-color: #cfdce5; border: 2px solid #7d9cb2; }
-body.usage table.cartouche { background-color: #e5e2b8; border: 2px solid #b2b27d; }
-body.extending table.cartouche { background-color: #e5cfcf; border: 2px solid #b27d7d; }
-body.internals table.cartouche { background-color: #decfe5; border: 2px solid #a17db2; }
-body.contributor table.cartouche { background-color: #e5e5e5; border: 2px solid #b2b2b2; }
-
table.cartouche p {
padding: 1em;
margin: 0;
}
#languages {
- padding: 0.5em 1em;
+ font-size: 0.8em;
+ padding: 0.9em 18px;
margin: 0;
}
margin-bottom: 1em;
}
+table th {
+ text-align: left;
+}
+
table td {
vertical-align: top;
padding-bottom: .5em;
padding: 0;
margin: 0;
overflow: auto;
- background: #dce35cf;
+ background: #657f40;
z-index: 100;
list-style-type: none;
font-size: 0.83em;
line-height: 1.3;
}
-body.learning div#tocframe { background-color: #cfe5cf; }
-body.notation div#tocframe { background-color: #cfdce5; }
-body.usage div#tocframe { background-color: #e5e5b8; }
-body.extending div#tocframe { background-color: #e5cfcf; }
-body.internals div#tocframe { background-color: #decfe5; }
-body.contributor div#tocframe { background-color: #e5e5e5; }
+body.learning #tocframe { background-color: #407f40; }
+body.notation #tocframe { background-color: #40657f; }
+body.usage #tocframe { background-color: #7d765a; }
+body.extending #tocframe { background-color: #7f4040; }
+body.internals #tocframe { background-color: #6a407f; }
+body.contributor #tocframe { background-color: #333333; }
@media screen {
body > div#tocframe {
}
}
-div#tocframe a:link, div#tocframe a:visited {
- color: black;
+div#tocframe a:link,
+div#tocframe a:visited {
+ color: #eee;
text-decoration: none;
}
div#tocframe a:hover {
- color: #666666;
+ color: #fff;
text-decoration: underline;
}
div#tocframe p.toc_uplink {
font-size: 1em;
line-height: 1.125;
- background: #c9ccc4;
padding: 0.25em 1em 0.25em 0.5em;
- border-bottom: 1px solid black;
margin: 0;
}
-div#tocframe p.toc_uplink a:link,
-div#tocframe p.toc_uplink a:visited {
- color: #1c1c1b;
-}
-
-div#tocframe p.toc_uplink a:hover {
- color: #000;
-}
-
div#tocframe h4 {
font-size: 1em;
line-height: 1.125;
.nav_table {
width: 100%;
- background: #c9ccc4;
+ background-color: #d5d7d6;
+ color: #505050;
font-size: 0.83em;
- border-collapse: collapse;
- padding: 0;
+ border-collapse: separate;
+ padding: 5px 18px;
border: none;
margin: 0;
}
-.nav_table a:link, .nav_table a:visited {
- color: #1c1c1b;
+.nav_table a:link,
+.nav_table a:visited {
+ color: #505050;
text-decoration: none;
}
text-decoration: underline;
}
-.nav_table tr, .nav_table a {
+.nav_table tr,
+.nav_table a {
padding: 0;
margin: 0;
}
.nav_table td {
- padding: 0.25em;
+ padding: 0.1em 0px;
margin: 0;
}
}
.footer {
- background: #657f40;
font-size: 0.8em;
padding: 0.2em 0;
- border: solid white;
- border-width: 0 0 5px 0;
margin: 0;
- color: white;
-}
-
-body.learning .footer { background-color: #407f40; }
-body.notation .footer { background-color: #40657f; }
-body.usage .footer { background-color: #7f7f33; }
-body.extending .footer { background-color: #7f4040; }
-body.internals .footer { background-color: #6a407f; }
-body.contributor .footer { background-color: #000000; }
-
-.footer a:link {
- color: white;
}
.footer p {
- padding: 0 1.25em;
+ padding: 0 18px;
margin: 0.4em 0;
}
background: #eef;
text-align: left;
padding: 0;
- border: 1px solid green;
- /* Experimental rounded corners */
- border-radius: 10px;
+ border: 1px solid #c8c8c8;
+ border-radius: 5px;
margin: 1em;
}
-body.learning .warning { border-color: #00ff00; }
-body.notation .warning { border-color: #0095ff; }
-body.usage .warning { border-color: #ffff00; }
-body.extending .warning { border-color: #ff0000; }
-body.internals .warning { border-color: #aa00ff; }
-body.contributor .warning { border-color: #000000; }
-
-
.advanced {
- background: #eeffcc;
+ background: #ffffc8;
text-align: left;
padding: 0;
- border: 1px solid green;
- /* Experimental rounded corners */
- border-radius: 10px;
+ border: 1px solid #c8c8c8;
+ border-radius: 5px;
margin: 0.5em 0.5em 2em 3em;
}
* Single-staff polyphony::
* Voice styles::
* Collision resolution::
+* Merging rests::
* Automatic part combining::
* Writing music in parallel::
@end menu
@end ignore
+@node Merging rests
+@unnumberedsubsubsec Merging rests
+
+When using multiple voices it is common to merge rests which occur in both
+parts. This can be accomplished using @code{Merge_rests_engraver}.
+
+@lilypond[quote,verbatim]
+voiceA = \relative { d''4 r d2 | R1 | }
+voiceB = \relative { fis'4 r g2 | R1 | }
+\score {
+ <<
+ \new Staff \with {
+ instrumentName = "unmerged"
+ }
+ <<
+ \new Voice { \voiceOne \voiceA }
+ \new Voice { \voiceTwo \voiceB }
+ >>
+ \new Staff \with {
+ instrumentName = "merged"
+ \consists #Merge_rests_engraver
+ }
+ <<
+ \new Voice { \voiceOne \voiceA }
+ \new Voice { \voiceTwo \voiceB }
+ >>
+ >>
+}
+@end lilypond
+
+Setting the context property @code{suspendRestMerging} to @code{##t} allows for
+turning off rest merging temporarily.
+
@node Automatic part combining
@unnumberedsubsubsec Automatic part combining
fi
])
+AC_DEFUN(STEPMAKE_PANGO_FT2_WITH_OTF_FEATURE, [
+ PKG_CHECK_MODULES(PANGO_FT2, $1 >= $3,
+ have_pangoft2_with_otf_feature=yes, true)
+ if test "$have_pangoft2_with_otf_feature" = yes ; then
+ AC_DEFINE(HAVE_PANGO16)
+ AC_DEFINE(HAVE_PANGO_FT2)
+ AC_DEFINE(HAVE_PANGO_FT2_WITH_OTF_FEATURE)
+ # Do not pollute user-CPPFLAGS with configure-CPPFLAGS
+ save_CPPFLAGS="$CPPFLAGS"
+ save_LIBS="$LIBS"
+ CPPFLAGS="$CPPFLAGS $PANGO_FT2_CFLAGS"
+ LIBS="$PANGO_FT2_LIBS $LIBS"
+ AC_CHECK_HEADERS([pango/pangoft2.h])
+ AC_CHECK_FUNCS([pango_ft2_font_map_create_context])
+ AC_SUBST(PANGO_FT2_CFLAGS)
+ AC_SUBST(PANGO_FT2_LIBS)
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+ else
+ # UGR
+ #r="lib$1-dev or $1-devel"e
+ r="libpango1.0-dev or pango?-devel"
+ ver="`pkg-config --modversion $1`"
+ STEPMAKE_ADD_ENTRY($2, ["$r >= $3 (It is required if you'd like "])
+ STEPMAKE_ADD_ENTRY($2, ["to use OpenType font feature. "])
+ STEPMAKE_ADD_ENTRY($2, ["installed: $ver)"])
+ fi
+])
+
AC_DEFUN(STEPMAKE_FONTCONFIG, [
PKG_CHECK_MODULES(FONTCONFIG, $1 >= $3, have_fontconfig=yes, true)
if test "$have_fontconfig" = yes ; then
/* define if you have pango FT2 binding */
#define HAVE_PANGO_FT2 0
+/* define if you have pango FT2 binding with OpenType font feature */
+#define HAVE_PANGO_FT2_WITH_OTF_FEATURE 0
+
/* define if Guile has types scm_t_hash_fold_fn and scm_t_hash_handle_fn */
#define HAVE_GUILE_HASH_FUNC 0
HOST_ARCH=`$CC -dumpmachine`
AC_SUBST(HOST_ARCH)
-STEPMAKE_PANGO_FT2(pangoft2, REQUIRED, 1.6.0)
+STEPMAKE_PANGO_FT2_WITH_OTF_FEATURE(pangoft2, OPTIONAL, 1.38.0)
+if test "$have_pangoft2_with_otf_feature" != yes ; then
+ STEPMAKE_PANGO_FT2(pangoft2, REQUIRED, 1.6.0)
+fi
STEPMAKE_FONTCONFIG(fontconfig, REQUIRED, 2.4.0)
STEPMAKE_FREETYPE2(freetype2, REQUIRED, 2.1.10)
--- /dev/null
+\version "2.19.60"
+
+\header
+{
+ texidoc = "Exercise font features. Requires a font that supports the
+ features. This ensures no errors using the interface."
+}
+
+% Comparison between caps styles
+\markup { Hello }
+\markup { HELLO }
+\markup { \caps Hello }
+\markup { \fontCaps Hello }
+% True small caps
+\markup { \override #'(font-features . ("smcp")) Hello }
+
+% Comparison between number styles
+\markup { 0123456789 }
+\markup { \override #'(font-features . ("onum")) 0123456789 }
+
+% Multiple features
+\markup { \override #'(font-features . ("onum" "smcp")) { Hello 0123456789 } }
--- /dev/null
+\version "2.19.60"
+
+\header {
+ texidoc = "Test for merging rests in different voices."
+}
+
+\paper {
+ ragged-right = ##f
+}
+
+voiceA = \relative {
+ % no rest merges
+ c''4 r c c |
+
+ % does not combine differently written rests
+ c4 r r2 |
+
+ % all rests merged
+ r2^"Up" r4 r8 r16 r32 r64 r128 r |
+
+ % multi-measure rests are combined
+ R1^"Upper text" |
+
+ % compressed multi-measure rests are combined
+ R1*3 |
+
+ % combining between beams, slurs
+ c8[( r c]) r c16[( r c] r c[ r c]) r |
+
+ % combining in tuplets
+ \tuplet 3/2 { c8 r r } r4 \tuplet 3/2 { c4 r r } |
+
+ % accents on rest, dynamics still aligned
+ r4->\f\> r-. r r\! |
+
+ % Non-multimeasure whole rests merged at the correct vertical position
+ \time 8/4
+ r1 r1
+
+ % Ensure when suspending merging rests are in their usual positions
+ \time 4/4
+ \set Staff.suspendRestMerging = ##t
+ r4 r8
+ \set Staff.suspendRestMerging = ##f
+ r8 r2 |
+
+ % Don't merge pitched rests
+ c4\rest d\rest e\rest f\rest |
+}
+
+voiceB = \relative {
+ r2 c'4 r |
+ c4 r r r |
+ r2_"Down" r4 r8 r16 r32 r64 r128 r |
+ R1_"Lower text" |
+ R1*3 |
+ c8[( r c]) r c16[( r c] r c[ r c]) r |
+ \tuplet 3/2 { c8 r r } r4 \tuplet 3/2 { c4 r r } |
+ r4-> r-. r r |
+ r1 r1 |
+ r4 r8 r r2 |
+ r4 r r r |
+}
+
+voiceC = \relative {
+ s1*2 |
+ r2 r4 r8 r16 r32 r64 r128 r | % Combines rests from more than 2 voices
+}
+
+\score {
+ \new Staff \with {
+ \consists #Merge_rests_engraver
+ } <<
+ \compressFullBarRests
+ \new Voice { \voiceOne \voiceA }
+ \new Voice { \voiceTwo \voiceB }
+ \new Voice { \voiceThree \voiceC }
+ >>
+}
"font-series "
"font-shape "
"font-size "
+ "font-features "
);
Stencil
Font_metric::text_stencil (Output_def *state,
- const string&, bool) const
+ const string&,
+ bool,
+ const string&) const
{
(void) state;
// Return stencil for given string. output_state may be modified to
// record the font.
virtual Stencil text_stencil (Output_def *output_state,
- const string &text, bool music) const;
+ const string &text,
+ bool music,
+ const string &features_str) const;
virtual string font_name () const;
virtual size_t count () const;
public Font_metric
{
public:
- Stencil text_stencil (Output_def *output_state, const string&, bool) const;
+ Stencil text_stencil (Output_def *output_state, const string&, bool, const string&) const;
Real get_magnification () const;
static SCM make_scaled_font_metric (Font_metric *fm, Real magnification);
Stencil pango_item_string_stencil (PangoGlyphItem const *) const;
virtual Stencil text_stencil (Output_def *output_state,
- const string &text, bool music) const;
+ const string &text,
+ bool music,
+ const string &features_str) const;
virtual void derived_mark () const;
};
Stencil
Modified_font_metric::text_stencil (Output_def *state,
- const string &text, bool feta) const
+ const string &text,
+ bool feta,
+ const string &features_str) const
{
Box b;
if (Pango_font *pf = dynamic_cast<Pango_font *> (orig_))
{
- Stencil stc = pf->text_stencil (state, text, feta);
+ Stencil stc = pf->text_stencil (state, text, feta, features_str);
Box b = stc.extent_box ();
return scaled;
}
- return Font_metric::text_stencil (state, text, feta);
+ return Font_metric::text_stencil (state, text, feta, features_str);
}
Font_metric *
Stencil
Pango_font::text_stencil (Output_def * /* state */,
- const string &str, bool music_string) const
+ const string &str,
+ bool music_string,
+ const string &features_str) const
{
/*
The text assigned to a PangoLayout is automatically divided
Bidirectional Algorithm, if necessary.
*/
PangoLayout *layout = pango_layout_new (context_);
+
+ if (!features_str.empty())
+ {
+#if HAVE_PANGO_FT2_WITH_OTF_FEATURE
+ PangoAttrList *list = pango_attr_list_new();
+ PangoAttribute *features_attr = pango_attr_font_features_new(features_str.c_str());
+ pango_attr_list_insert(list, features_attr);
+ pango_layout_set_attributes(layout, list);
+ pango_attr_list_unref(list);
+#else
+ warning (_f ("OpenType font feature `%s' cannot be used"
+ " since this binary is configured without feature support.",
+ features_str.c_str ()));
+#endif
+ }
+
pango_layout_set_text (layout, str.c_str (), -1);
GSList *lines = pango_layout_get_lines (layout);
SCM_BOOL_F);
SCM music_encodings = Lily::all_music_font_encodings;
+ SCM features = ly_chain_assoc_get (ly_symbol2scm ("font-features"),
+ props,
+ SCM_BOOL_F);
+
+ // The font-features value is stored in a scheme list. This joins the entries
+ // with commas for processing with pango.
+ string features_str = string ();
+ if (scm_is_pair (features))
+ {
+ bool first = true;
+ for (SCM s = features; scm_is_pair (s); s = scm_cdr (s))
+ {
+ SCM feature = scm_car (s);
+ if (scm_is_string (feature))
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ features_str += ",";
+ }
+ features_str += ly_scm2string (feature);
+ }
+ else
+ {
+ scm_misc_error (__FUNCTION__, "Found non-string in font-features list", SCM_EOL);
+ }
+ }
+ }
+ else if (!scm_is_false (features))
+ {
+ scm_misc_error (__FUNCTION__, "Expecting a list for font-features value", SCM_EOL);
+ }
+
bool is_music = scm_is_true (scm_memq (encoding, music_encodings));
- return fm->text_stencil (layout, str, is_music).smobbed_copy ();
+ return fm->text_stencil (layout, str, is_music, features_str).smobbed_copy ();
}
static size_t markup_depth = 0;
@noindent
This will create a start-repeat bar in this staff only. Valid values
are described in @file{scm/bar-line.scm}.")
+ (suspendRestMerging ,boolean? "When using the Merge_rest_engraver do not
+ merge rests when this is set to true.")
)))
larger. If the context property @code{fontSize} is set, its value is
added to this before the glyph is printed. Fractional values are
allowed.")
+ (font-features ,list? "Opentype features.")
(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.")
(properties-read . ())
(properties-written . ())
(description . "Connect cross-staff stems to the stems above in the system")))
+
+(define-public (Merge_rests_engraver context)
+"Engraver to merge rests in multiple voices on the same staff.
+
+This works by gathering all rests at a time step. If they are all of the same
+length and there are at least two they are moved to the correct location as
+if there were one voice."
+
+ (define (is-single-bar-rest? mmrest)
+ (eqv? (ly:grob-property mmrest 'measure-count) 1))
+
+ (define (is-whole-rest? rest)
+ (eqv? (ly:grob-property rest 'duration-log) 0))
+
+ (define (mmrest-offset mmrest)
+ "For single measures they should hang from the second line from the top
+ (offset of 1). For longer multimeasure rests they should be centered on the
+ middle line (offset of 0).
+ NOTE: For one-line staves full single measure rests should be positioned at
+ 0, but I don't anticipate this engraver's use in that case. No errors are
+ given in this case."
+ (if (is-single-bar-rest? mmrest) 1 0))
+
+ (define (rest-offset rest)
+ (if (is-whole-rest? rest) 1 0))
+
+ (define (rest-eqv rest-len-prop)
+ "Compare rests according the given property"
+ (define (rest-len rest) (ly:grob-property rest rest-len-prop))
+ (lambda (rest-a rest-b)
+ (eqv? (rest-len rest-a) (rest-len rest-b))))
+
+ (define (rests-all-unpitched rests)
+ "Returns true when all rests do not override the staff-position grob
+ property. When a rest has a position set we do not want to merge rests at
+ that position."
+ (every (lambda (rest) (null? (ly:grob-property rest 'staff-position))) rests))
+
+ (define (merge-mmrests rests)
+ "Move all multimeasure rests to the single voice location."
+ (if (all-equal rests (rest-eqv 'measure-count))
+ (merge-rests rests mmrest-offset)))
+
+ (define (merge-rests rests offset-function)
+ (let ((y-offset (offset-function (car rests))))
+ (for-each
+ (lambda (rest) (ly:grob-set-property! rest 'Y-offset y-offset))
+ rests))
+ (for-each
+ (lambda (rest) (ly:grob-set-property! rest 'transparent #t))
+ (cdr rests)))
+
+ (define has-one-or-less (lambda (lst) (or (null? lst) (null? (cdr lst)))))
+ (define has-at-least-two (lambda (lst) (not (has-one-or-less lst))))
+ (define (all-equal lst pred)
+ (or (has-one-or-less lst)
+ (and (pred (car lst) (cadr lst)) (all-equal (cdr lst) pred))))
+
+ (let ((curr-mmrests '())
+ (mmrests '())
+ (rests '()))
+ (make-engraver
+ ((start-translation-timestep translator)
+ (set! rests '())
+ (set! curr-mmrests '()))
+ (acknowledgers
+ ((rest-interface engraver grob source-engraver)
+ (cond
+ ((ly:context-property context 'suspendRestMerging #f)
+ #f)
+ ((grob::has-interface grob 'multi-measure-rest-interface)
+ (set! curr-mmrests (cons grob curr-mmrests)))
+ (else
+ (set! rests (cons grob rests))))))
+ ((stop-translation-timestep translator)
+ (if (and
+ (has-at-least-two rests)
+ (all-equal rests (rest-eqv 'duration-log))
+ (rests-all-unpitched rests))
+ (merge-rests rests rest-offset))
+ (if (has-at-least-two curr-mmrests)
+ (set! mmrests (cons curr-mmrests mmrests))))
+ ((finalize translator)
+ (for-each merge-mmrests mmrests)))))