]> git.donarmstrong.com Git - lilypond.git/commitdiff
Merge branch 'master' into translation
authorJean-Charles Malahieude <lilyfan@orange.fr>
Sat, 24 Jun 2017 11:39:43 +0000 (13:39 +0200)
committerJean-Charles Malahieude <lilyfan@orange.fr>
Sat, 24 Jun 2017 11:39:43 +0000 (13:39 +0200)
18 files changed:
Documentation/css/lilypond-manuals.css
Documentation/notation/simultaneous.itely
aclocal.m4
config.hh.in
configure.ac
input/regression/font-features.ly [new file with mode: 0644]
input/regression/merge-rests-engraver.ly [new file with mode: 0644]
lily/font-interface.cc
lily/font-metric.cc
lily/include/font-metric.hh
lily/include/modified-font-metric.hh
lily/include/pango-font.hh
lily/modified-font-metric.cc
lily/pango-font.cc
lily/text-interface.cc
scm/define-context-properties.scm
scm/define-grob-properties.scm
scm/scheme-engravers.scm

index e0e8eca9b6ab68c5dafe5907e3287d8ed1e70abd..d444792955d54edb875b65b5f6b153716863174e 100644 (file)
@@ -2,10 +2,14 @@
 /*                  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
@@ -13,7 +17,7 @@
    essay
 
    notation          blue           205
-   usage             yellow         50
+   usage             brown
    snippets
 
    changes
@@ -36,7 +40,7 @@ body {
   height: 100%;
   font-size: 100%;
   line-height: 1.125;
-  color: #000;
+  color: #222;
   background-color: #fff;
 }
 
@@ -55,23 +59,12 @@ body {
 }
 
 .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,
@@ -127,22 +120,17 @@ a:hover {
 /*                  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;
@@ -167,21 +155,14 @@ blockquote blockquote {
 }
 
 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;
@@ -225,7 +206,8 @@ p {
 }
 
 #languages {
-  padding: 0.5em 1em;
+  font-size: 0.8em;
+  padding: 0.9em 18px;
   margin: 0;
 }
 
@@ -244,6 +226,10 @@ pre.menu-comment {
   margin-bottom: 1em;
 }
 
+table th {
+  text-align: left;
+}
+
 table td {
     vertical-align: top;
     padding-bottom: .5em;
@@ -262,19 +248,19 @@ div#tocframe {
   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 {
@@ -282,34 +268,24 @@ body.contributor div#tocframe { background-color: #e5e5e5; }
   }
 }
 
-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;
@@ -368,16 +344,18 @@ li.toc_current ul {
 
 .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;
 }
 
@@ -386,13 +364,14 @@ li.toc_current ul {
   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;
 }
 
@@ -443,28 +422,13 @@ table .title {
 }
 
 .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;
 }
 
@@ -518,26 +482,16 @@ div#search p, div#search form {
   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;
 }
index c9810d62c305f127fb1255ba48188372b4f518a6..1ad24d94b9f7a260d3f653659f466b8ac92c949f 100644 (file)
@@ -389,6 +389,7 @@ multiple staves.
 * Single-staff polyphony::
 * Voice styles::
 * Collision resolution::
+* Merging rests::
 * Automatic part combining::
 * Writing music in parallel::
 @end menu
@@ -909,6 +910,39 @@ are at the same time differently dotted are not clear.
 @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
 
index b15111dd0c097c640f4f737b73d6ee7c4f76d219..1228f6ca29c6208a89c19bdf8aee3f651a9c3dcb 100644 (file)
@@ -1344,6 +1344,35 @@ AC_DEFUN(STEPMAKE_PANGO_FT2, [
     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
index 800d77d8a27f71473014c5f57af036aa389d3c2f..a093ad83b2b0b7d8824d33841991fc5e62cd8d2e 100644 (file)
@@ -89,6 +89,9 @@
 /* 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
 
index 8f2f61abf810193486dcaedb5cdac715a8ac09e1..11addfb70044067bb4966f864519f2113a2ffb1f 100644 (file)
@@ -260,7 +260,10 @@ AC_MSG_RESULT($rpath_b)
 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)
 
diff --git a/input/regression/font-features.ly b/input/regression/font-features.ly
new file mode 100644 (file)
index 0000000..ed01c1f
--- /dev/null
@@ -0,0 +1,22 @@
+\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 } }
diff --git a/input/regression/merge-rests-engraver.ly b/input/regression/merge-rests-engraver.ly
new file mode 100644 (file)
index 0000000..688fc31
--- /dev/null
@@ -0,0 +1,79 @@
+\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 }
+  >>
+}
index de787fd852bf2e4ff04a1fdb96d80d54e412dc89..5e5f18ceb3f8a83d133820d762228c06a0cc44ad 100644 (file)
@@ -72,4 +72,5 @@ ADD_INTERFACE (Font_interface,
                "font-series "
                "font-shape "
                "font-size "
+               "font-features "
               );
index c9daa95f3edf2000d39fb47a5d856a8df505073a..77f02df579c9beaedc747dcf7d565f4e00c1d5ed 100644 (file)
@@ -147,7 +147,9 @@ Font_metric::sub_fonts () const
 
 Stencil
 Font_metric::text_stencil (Output_def *state,
-                           const string&, bool) const
+                           const string&,
+                           bool,
+                           const string&) const
 {
   (void) state;
 
index 7a388f86f35c1031f59c023805802b5c973c9cfc..429801db5520800c1043afafc43306b39346f303 100644 (file)
@@ -47,7 +47,9 @@ public:
   // 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;
index 0f4223d4ba90779d781a1720f886c55b273bd326..70016808633d6c0461f3a3006443f961fc55f779 100644 (file)
@@ -33,7 +33,7 @@ class Modified_font_metric : Preinit_Modified_font_metric,
                               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);
index aaae077b769d01bd257e1e96414581a6ea147dd4..5e804f7a8f8f743430dd0c757d3220bd9df5619b 100644 (file)
@@ -63,7 +63,9 @@ public:
   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;
 };
 
index d68db92c4b897b6728ee0d37fec25410360a2f6e..a3beb4daefa0665be7fd0bacef73fe19571e49ef 100644 (file)
@@ -113,12 +113,14 @@ Modified_font_metric::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 ();
 
@@ -127,7 +129,7 @@ Modified_font_metric::text_stencil (Output_def *state,
       return scaled;
     }
 
-  return Font_metric::text_stencil (state, text, feta);
+  return Font_metric::text_stencil (state, text, feta, features_str);
 }
 
 Font_metric *
index 9fe6b78cb00a54a78273b5820c5bfe672ea10136..1a80ee989c7628fa64206c553494d264bc5a60ca 100644 (file)
@@ -387,7 +387,9 @@ extern bool music_strings_to_paths;
 
 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
@@ -395,6 +397,22 @@ Pango_font::text_stencil (Output_def * /* state */,
     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);
 
index d930cdaa372868622e4624883c4ace2f5539e2a9..bad6d50281f626ccce966a8e15200b9a4f80ea8d 100644 (file)
@@ -92,8 +92,44 @@ Text_interface::interpret_string (SCM layout_smob,
                                      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;
index c79d7c94ab44a461ccf8e568358fa6cb275508b2..04b57afaceef21496507df5ab01a7a0d8ae9a759 100644 (file)
@@ -673,6 +673,8 @@ Example:
 @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.")
      )))
 
 
index fbcaa2b4bb85e74812e5b56a898d201417df33e7..0acc741f1cb93ad13ffa151b8fafc745df9037f0 100644 (file)
@@ -327,6 +327,7 @@ approximately 12% larger; 6@tie{}steps are exactly a factor@tie{}2
 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.")
index f4902cb94b99b2c31b05f064efca75bb8df13c01..b2966d79e61281e985f8af35d4ca475ef074a2b8 100644 (file)
@@ -117,3 +117,87 @@ receive a count with @code{\\startMeasureCount} and
    (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)))))