]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/paper-book.cc
the distributed help2man is outdated and sucky. Use the Debian version instead.
[lilypond.git] / lily / paper-book.cc
index 168d19b92e899eb85faacb2caf997be69948712d..aecafbe3a053d66a15d884684ea570e01c011a85 100644 (file)
@@ -1,20 +1,35 @@
 /*
 /*
-  paper-book.cc -- implement Paper_book
+  This file is part of LilyPond, the GNU music typesetter.
 
 
-  source file of the GNU LilyPond music typesetter
+  Copyright (C) 2004--2012 Jan Nieuwenhuizen <janneke@gnu.org>
 
 
-  (c) 2004--2006 Jan Nieuwenhuizen <janneke@gnu.org>
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "paper-book.hh"
 
 #include "grob.hh"
 */
 
 #include "paper-book.hh"
 
 #include "grob.hh"
+#include "international.hh"
 #include "main.hh"
 #include "output-def.hh"
 #include "main.hh"
 #include "output-def.hh"
+#include "paper-column.hh"
 #include "paper-score.hh"
 #include "paper-system.hh"
 #include "text-interface.hh"
 #include "warn.hh"
 #include "paper-score.hh"
 #include "paper-system.hh"
 #include "text-interface.hh"
 #include "warn.hh"
+#include "program-option.hh"
+#include "page-marker.hh"
 
 #include "ly-smobs.icc"
 
 
 #include "ly-smobs.icc"
 
@@ -24,10 +39,12 @@ Paper_book::Paper_book ()
   header_0_ = SCM_EOL;
   pages_ = SCM_BOOL_F;
   scores_ = SCM_EOL;
   header_0_ = SCM_EOL;
   pages_ = SCM_BOOL_F;
   scores_ = SCM_EOL;
+  bookparts_ = SCM_EOL;
   performances_ = SCM_EOL;
   systems_ = SCM_BOOL_F;
 
   paper_ = 0;
   performances_ = SCM_EOL;
   systems_ = SCM_BOOL_F;
 
   paper_ = 0;
+  parent_ = 0;
   smobify_self ();
 }
 
   smobify_self ();
 }
 
@@ -45,16 +62,19 @@ Paper_book::mark_smob (SCM smob)
   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
   if (b->paper_)
     scm_gc_mark (b->paper_->self_scm ());
   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
   if (b->paper_)
     scm_gc_mark (b->paper_->self_scm ());
+  if (b->parent_)
+    scm_gc_mark (b->parent_->self_scm ());
   scm_gc_mark (b->header_);
   scm_gc_mark (b->header_0_);
   scm_gc_mark (b->pages_);
   scm_gc_mark (b->performances_);
   scm_gc_mark (b->scores_);
   scm_gc_mark (b->header_);
   scm_gc_mark (b->header_0_);
   scm_gc_mark (b->pages_);
   scm_gc_mark (b->performances_);
   scm_gc_mark (b->scores_);
+  scm_gc_mark (b->bookparts_);
   return b->systems_;
 }
 
 int
   return b->systems_;
 }
 
 int
-Paper_book::print_smob (SCM smob, SCM port, scm_print_state*)
+Paper_book::print_smob (SCM smob, SCM port, scm_print_state *)
 {
   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
   (void)b;
 {
   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
   (void)b;
@@ -62,6 +82,15 @@ Paper_book::print_smob (SCM smob, SCM port, scm_print_state*)
   return 1;
 }
 
   return 1;
 }
 
+Output_def *
+Paper_book::top_paper ()
+{
+  Output_def *paper = paper_;
+  while (paper->parent_)
+    paper = paper->parent_;
+  return paper;
+}
+
 SCM
 dump_fields ()
 {
 SCM
 dump_fields ()
 {
@@ -69,7 +98,7 @@ dump_fields ()
   for (vsize i = dump_header_fieldnames_global.size (); i--;)
     fields
       = scm_cons (ly_symbol2scm (dump_header_fieldnames_global[i].c_str ()),
   for (vsize i = dump_header_fieldnames_global.size (); i--;)
     fields
       = scm_cons (ly_symbol2scm (dump_header_fieldnames_global[i].c_str ()),
-                 fields);
+                  fields);
   return fields;
 }
 
   return fields;
 }
 
@@ -79,64 +108,156 @@ Paper_book::add_score (SCM s)
   scores_ = scm_cons (s, scores_);
 }
 
   scores_ = scm_cons (s, scores_);
 }
 
+void
+Paper_book::add_bookpart (SCM p)
+{
+  bookparts_ = scm_cons (p, bookparts_);
+}
+
 void
 Paper_book::add_performance (SCM s)
 {
   performances_ = scm_cons (s, performances_);
 }
 
 void
 Paper_book::add_performance (SCM s)
 {
   performances_ = scm_cons (s, performances_);
 }
 
-void
-Paper_book::output (SCM output_channel)
+long
+Paper_book::output_aux (SCM output_channel,
+                        bool is_last,
+                        long *first_page_number,
+                        long *first_performance_number)
 {
 {
+  long page_nb = 0;
   if (scm_is_pair (performances_))
     {
   if (scm_is_pair (performances_))
     {
-      SCM proc = ly_lily_module_constant ("paper-book-write-midis");
+      SCM proc = ly_lily_module_constant ("write-performances-midis");
 
 
-      scm_call_2 (proc, self_scm (), output_channel);
+      scm_call_3 (proc,
+                  performances (),
+                  output_channel,
+                  scm_from_long (*first_performance_number));
+      *first_performance_number += scm_ilength (performances_);
     }
 
     }
 
-  if (scores_ == SCM_EOL)
-    return;
+  if (scm_is_pair (bookparts_))
+    {
+      for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
+        if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
+          {
+            bool is_last_part = (is_last && !scm_is_pair (scm_cdr (p)));
+            page_nb += pbookpart->output_aux (output_channel,
+                                              is_last_part,
+                                              first_page_number,
+                                              first_performance_number);
+          }
+    }
+  else
+    {
+      if (scores_ == SCM_EOL)
+        return 0;
+      paper_->set_variable (ly_symbol2scm ("first-page-number"),
+                            scm_from_long (*first_page_number));
+      paper_->set_variable (ly_symbol2scm ("is-last-bookpart"),
+                            ly_bool2scm (is_last));
+      /* Generate all stencils to trigger font loads.  */
+      page_nb = scm_ilength (pages ());
+      *first_page_number += page_nb;
+    }
+  return page_nb;
+}
 
 
-  /* Generate all stencils to trigger font loads.  */
-  pages ();
+void
+Paper_book::output (SCM output_channel)
+{
+  long first_page_number
+    = robust_scm2int (paper_->c_variable ("first-page-number"), 1);
+  long first_performance_number = 0;
+
+  /* FIXME: We need a line-width for ps output (framework-ps.scm:92).
+     If we don't have any, we take the paper-width unless we know
+     better which line-width to choose (e.g. if there are \bookparts
+     with different line-widths) and why we need it at all.
+  */
+
+  if (paper_->c_variable ("line-width") == SCM_UNDEFINED)
+    paper_->set_variable (ly_symbol2scm ("line-width"),
+                          paper_->c_variable ("paper-width"));
+
+  if (!output_aux (output_channel,
+                   true,
+                   &first_page_number,
+                   &first_performance_number))
+    return;
 
   SCM scopes = SCM_EOL;
   if (ly_is_module (header_))
     scopes = scm_cons (header_, scopes);
 
 
   SCM scopes = SCM_EOL;
   if (ly_is_module (header_))
     scopes = scm_cons (header_, scopes);
 
-  string mod_nm = "scm framework-" + output_backend_global;
+  string mod_nm = "scm framework-" + get_output_backend_name ();
 
   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
 
   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
-  if (make_print)
+
+  if (get_program_option ("print-pages"))
     {
     {
-      SCM func = scm_c_module_lookup (mod, "output-framework");
-
-      func = scm_variable_ref (func);
-      scm_apply_0 (func, scm_list_n (output_channel,
-                                    self_scm (),
-                                    scopes,
-                                    dump_fields (),
-                                    SCM_UNDEFINED));
+      SCM framework = ly_module_lookup (mod,
+                                        ly_symbol2scm ("output-framework"));
+
+      if (framework != SCM_BOOL_F)
+        {
+          SCM func = scm_variable_ref (framework);
+          scm_apply_0 (func, scm_list_n (output_channel,
+                                         self_scm (),
+                                         scopes,
+                                         dump_fields (),
+                                         SCM_UNDEFINED));
+        }
+      else
+        warning (_f ("program option -dprint-pages not supported by backend `%s'",
+                     get_output_backend_name ()));
     }
 
     }
 
-  if (make_preview)
+  if (get_program_option ("preview"))
     {
     {
-      SCM func = scm_c_module_lookup (mod, "output-preview-framework");
-      func = scm_variable_ref (func);
-      scm_apply_0 (func, scm_list_n (output_channel,
-                                    self_scm (),
-                                    scopes,
-                                    dump_fields (),
-                                    SCM_UNDEFINED));
+      SCM framework
+        = ly_module_lookup (mod, ly_symbol2scm ("output-preview-framework"));
+
+      if (framework != SCM_BOOL_F)
+        {
+          SCM func = scm_variable_ref (framework);
+          scm_apply_0 (func, scm_list_n (output_channel,
+                                         self_scm (),
+                                         scopes,
+                                         dump_fields (),
+                                         SCM_UNDEFINED));
+        }
+      else
+        warning (_f ("program option -dpreview not supported by backend `%s'",
+                     get_output_backend_name ()));
     }
 }
 
 void
     }
 }
 
 void
-Paper_book::classic_output (SCM output)
+Paper_book::classic_output_aux (SCM output,
+                                long *first_performance_number)
 {
 {
+  if (scm_is_pair (performances_))
+    {
+      SCM proc = ly_lily_module_constant ("write-performances-midis");
+      scm_call_3 (proc,
+                  performances (),
+                  output,
+                  scm_from_long (*first_performance_number));
+      *first_performance_number += scm_ilength (performances_);
+    }
+
   /* Generate all stencils to trigger font loads.  */
   systems ();
   /* Generate all stencils to trigger font loads.  */
   systems ();
+}
+
+void
+Paper_book::classic_output (SCM output)
+{
+  long first_performance_number = 0;
+  classic_output_aux (output, &first_performance_number);
 
   SCM scopes = SCM_EOL;
   if (ly_is_module (header_))
 
   SCM scopes = SCM_EOL;
   if (ly_is_module (header_))
@@ -145,7 +266,7 @@ Paper_book::classic_output (SCM output)
   if (ly_is_module (header_0_))
     scopes = scm_cons (header_0_, scopes);
 
   if (ly_is_module (header_0_))
     scopes = scm_cons (header_0_, scopes);
 
-  string format = output_backend_global;
+  string format = get_output_backend_name ();
   string mod_nm = "scm framework-" + format;
 
   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
   string mod_nm = "scm framework-" + format;
 
   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
@@ -153,10 +274,10 @@ Paper_book::classic_output (SCM output)
 
   func = scm_variable_ref (func);
   scm_apply_0 (func, scm_list_n (output,
 
   func = scm_variable_ref (func);
   scm_apply_0 (func, scm_list_n (output,
-                                self_scm (),
-                                scopes,
-                                dump_fields (),
-                                SCM_UNDEFINED));
+                                 self_scm (),
+                                 scopes,
+                                 dump_fields (),
+                                 SCM_UNDEFINED));
 
   progress_indication ("\n");
 }
 
   progress_indication ("\n");
 }
@@ -175,8 +296,8 @@ Paper_book::book_title ()
   SCM tit = SCM_EOL;
   if (ly_is_procedure (title_func))
     tit = scm_call_2 (title_func,
   SCM tit = SCM_EOL;
   if (ly_is_procedure (title_func))
     tit = scm_call_2 (title_func,
-                     paper_->self_scm (),
-                     scopes);
+                      paper_->self_scm (),
+                      scopes);
 
   if (unsmob_stencil (tit))
     title = *unsmob_stencil (tit);
 
   if (unsmob_stencil (tit))
     title = *unsmob_stencil (tit);
@@ -204,8 +325,8 @@ Paper_book::score_title (SCM header)
   SCM tit = SCM_EOL;
   if (ly_is_procedure (title_func))
     tit = scm_call_2 (title_func,
   SCM tit = SCM_EOL;
   if (ly_is_procedure (title_func))
     tit = scm_call_2 (title_func,
-                     paper_->self_scm (),
-                     scopes);
+                      paper_->self_scm (),
+                      scopes);
 
   if (unsmob_stencil (tit))
     title = *unsmob_stencil (tit);
 
   if (unsmob_stencil (tit))
     title = *unsmob_stencil (tit);
@@ -216,10 +337,27 @@ Paper_book::score_title (SCM header)
   return title;
 }
 
   return title;
 }
 
+void
+set_page_permission (SCM sys, SCM symbol, SCM permission)
+{
+  if (Paper_score *ps = dynamic_cast<Paper_score *> (unsmob_music_output (sys)))
+    {
+      vector<Grob *> cols = ps->get_columns ();
+      if (cols.size ())
+        {
+          Paper_column *col = dynamic_cast<Paper_column *> (cols.back ());
+          col->set_property (symbol, permission);
+          col->find_prebroken_piece (LEFT)->set_property (symbol, permission);
+        }
+    }
+  else if (Prob *pb = unsmob_prob (sys))
+    pb->set_property (symbol, permission);
+}
+
 /* read the breakbefore property of a score block and set up the preceding
 /* read the breakbefore property of a score block and set up the preceding
-   system-spec to honour it. That is, SYM should be the system spec that
+   system-spec to honour it. That is, SYS should be the system spec that
    immediately precedes the score (from which HEADER is taken)
    immediately precedes the score (from which HEADER is taken)
-   in the get_system_specs() list */
+   in the get_system_specs () list */
 void
 set_system_penalty (SCM sys, SCM header)
 {
 void
 set_system_penalty (SCM sys, SCM header)
 {
@@ -227,23 +365,46 @@ set_system_penalty (SCM sys, SCM header)
     {
       SCM force = ly_module_lookup (header, ly_symbol2scm ("breakbefore"));
       if (SCM_VARIABLEP (force)
     {
       SCM force = ly_module_lookup (header, ly_symbol2scm ("breakbefore"));
       if (SCM_VARIABLEP (force)
-         && scm_is_bool (SCM_VARIABLE_REF (force)))
-       {
-         bool b = to_boolean (SCM_VARIABLE_REF (force));
-         SCM sym = b ? ly_symbol2scm ("force") : SCM_EOL;
-
-         if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (sys)))
-           {
-             vector<Grob*> cols = ps->get_columns ();
-             if (cols.size ())
-               cols.back ()->set_property ("page-break-permission", sym);
-           }
-         else if (Prob *pb = unsmob_prob (sys))
-           pb->set_property ("page-break-permission", sym);
-       }
+          && scm_is_bool (SCM_VARIABLE_REF (force)))
+        {
+          if (to_boolean (SCM_VARIABLE_REF (force)))
+            {
+              set_page_permission (sys, ly_symbol2scm ("page-break-permission"),
+                                   ly_symbol2scm ("force"));
+              set_page_permission (sys, ly_symbol2scm ("line-break-permission"),
+                                   ly_symbol2scm ("force"));
+            }
+          else
+            set_page_permission (sys, ly_symbol2scm ("page-break-permission"),
+                                 SCM_EOL);
+        }
     }
 }
 
     }
 }
 
+void
+set_labels (SCM sys, SCM labels)
+{
+  if (Paper_score *ps = dynamic_cast<Paper_score *> (unsmob_music_output (sys)))
+    {
+      vector<Grob *> cols = ps->get_columns ();
+      if (cols.size ())
+        {
+          Paper_column *col = dynamic_cast<Paper_column *> (cols[0]);
+          col->set_property ("labels",
+                             scm_append_x (scm_list_2 (col->get_property ("labels"),
+                                                       labels)));
+          Paper_column *col_right
+            = dynamic_cast<Paper_column *> (col->find_prebroken_piece (RIGHT));
+          col_right->set_property ("labels",
+                                   scm_append_x (scm_list_2 (col_right->get_property ("labels"),
+                                                             labels)));
+        }
+    }
+  else if (Prob *pb = unsmob_prob (sys))
+    pb->set_property ("labels",
+                      scm_append_x (scm_list_2 (pb->get_property ("labels"),
+                                                labels)));
+}
 
 SCM
 Paper_book::get_score_title (SCM header)
 
 SCM
 Paper_book::get_score_title (SCM header)
@@ -254,29 +415,30 @@ Paper_book::get_score_title (SCM header)
   if (!title.is_empty ())
     {
       /*
   if (!title.is_empty ())
     {
       /*
-       TODO: this should come from the \layout {} block, which should
-       override settings from \paper {}
+        TODO: this should come from the \layout {} block, which should
+        override settings from \paper {}
       */
       */
-      SCM props = paper_->lookup_variable (ly_symbol2scm ("score-title-properties"));
+      SCM props
+        = paper_->lookup_variable (ly_symbol2scm ("score-title-properties"));
       Prob *ps = make_paper_system (props);
       paper_system_set_stencil (ps, title);
 
       Prob *ps = make_paper_system (props);
       paper_system_set_stencil (ps, title);
 
-      return ps->self_scm();
+      return ps->self_scm ();
     }
 
   return SCM_BOOL_F;
 }
 
     }
 
   return SCM_BOOL_F;
 }
 
-
 SCM
 Paper_book::get_system_specs ()
 {
   SCM system_specs = SCM_EOL;
 SCM
 Paper_book::get_system_specs ()
 {
   SCM system_specs = SCM_EOL;
-  
+
   Stencil title = book_title ();
   if (!title.is_empty ())
     {
   Stencil title = book_title ();
   if (!title.is_empty ())
     {
-      SCM props = paper_->lookup_variable (ly_symbol2scm ("book-title-properties"));
+      SCM props
+        = paper_->lookup_variable (ly_symbol2scm ("book-title-properties"));
       Prob *ps = make_paper_system (props);
       paper_system_set_stencil (ps, title);
 
       Prob *ps = make_paper_system (props);
       paper_system_set_stencil (ps, title);
 
@@ -286,60 +448,123 @@ Paper_book::get_system_specs ()
 
   SCM page_properties
     = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
 
   SCM page_properties
     = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
-                 paper_->self_scm ());
+                  paper_->self_scm ());
 
 
+  SCM interpret_markup_list = ly_lily_module_constant ("interpret-markup-list");
   SCM header = SCM_EOL;
   SCM header = SCM_EOL;
+  SCM labels = SCM_EOL;
   for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
     {
       if (ly_is_module (scm_car (s)))
   for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
     {
       if (ly_is_module (scm_car (s)))
-       {
-         header = scm_car (s);
-         if (header_0_ == SCM_EOL)
-           header_0_ = header;
-       }
+        {
+          header = scm_car (s);
+          if (header_0_ == SCM_EOL)
+            header_0_ = header;
+        }
+      else if (Page_marker *page_marker = unsmob_page_marker (scm_car (s)))
+        {
+          /* page markers are used to set page breaking/turning permission,
+             or to place bookmarking labels */
+          if (scm_is_symbol (page_marker->permission_symbol ()))
+            {
+              /* set previous element page break or turn permission */
+              if (scm_is_pair (system_specs))
+                set_page_permission (scm_car (system_specs),
+                                     page_marker->permission_symbol (),
+                                     page_marker->permission_value ());
+            }
+          if (scm_is_symbol (page_marker->label ()))
+            {
+              /* The next element label is to be set */
+              labels = scm_cons (page_marker->label (), labels);
+            }
+        }
       else if (Music_output *mop = unsmob_music_output (scm_car (s)))
       else if (Music_output *mop = unsmob_music_output (scm_car (s)))
-       {
-         if (Paper_score *pscore = dynamic_cast<Paper_score *> (mop))
-           {
-             SCM title = get_score_title (header);
-
-             if (scm_is_pair (system_specs))
-               set_system_penalty (scm_car (system_specs), header);
-
-             if (unsmob_prob (title))
-               {
-                 system_specs = scm_cons (title, system_specs);
-                 unsmob_prob (title)->unprotect ();
-               }
-
-             header = SCM_EOL;
-             system_specs = scm_cons (pscore->self_scm (), system_specs);
-           }
-         else
-           {
-             /*
-               Ignore MIDI
-             */
-           }
-       }
-      else if (Text_interface::is_markup (scm_car (s)))
-       {
-         SCM t = Text_interface::interpret_markup (paper_->self_scm (),
-                                                   page_properties,
-                                                   scm_car (s));
-         
-         // TODO: init props
-         Prob *ps = make_paper_system (SCM_EOL);
-         paper_system_set_stencil (ps, *unsmob_stencil (t));
-         ps->set_property ("is-title", SCM_BOOL_T); 
-         system_specs = scm_cons (ps->self_scm (), system_specs);
-         ps->unprotect ();
-         
-         // FIXME: figure out penalty.
-         //set_system_penalty (ps, scores_[i].header_);
-       }
+        {
+          if (Paper_score *pscore = dynamic_cast<Paper_score *> (mop))
+            {
+              SCM title = get_score_title (header);
+
+              if (scm_is_pair (system_specs))
+                set_system_penalty (scm_car (system_specs), header);
+
+              if (unsmob_prob (title))
+                {
+                  system_specs = scm_cons (title, system_specs);
+                  unsmob_prob (title)->unprotect ();
+                }
+
+              header = SCM_EOL;
+              system_specs = scm_cons (pscore->self_scm (), system_specs);
+              if (scm_is_pair (labels))
+                {
+                  set_labels (scm_car (system_specs), labels);
+                  labels = SCM_EOL;
+                }
+            }
+          else
+            {
+              /*
+                Ignore MIDI
+              */
+            }
+        }
+      else if (Text_interface::is_markup_list (scm_car (s)))
+        {
+          SCM texts = scm_call_3 (interpret_markup_list,
+                                  paper_->self_scm (),
+                                  page_properties,
+                                  scm_car (s));
+          Prob *first = 0;
+          Prob *last = 0;
+          for (SCM list = texts; scm_is_pair (list); list = scm_cdr (list))
+            {
+              SCM t = scm_car (list);
+              // TODO: init props
+              Prob *ps = make_paper_system (SCM_EOL);
+              ps->set_property ("page-break-permission",
+                                ly_symbol2scm ("allow"));
+              ps->set_property ("page-turn-permission",
+                                ly_symbol2scm ("allow"));
+              ps->set_property ("last-markup-line", SCM_BOOL_F);
+              ps->set_property ("first-markup-line", SCM_BOOL_F);
+
+              paper_system_set_stencil (ps, *unsmob_stencil (t));
+
+              SCM footnotes = get_footnotes (unsmob_stencil (t)->expr ());
+              ps->set_property ("footnotes", footnotes);
+              ps->set_property ("is-title", SCM_BOOL_T);
+              if (list == texts)
+                first = ps;
+              else
+                {
+                  // last line so far, in a multi-line paragraph
+                  last = ps;
+                  //Place closely to previous line, no stretching.
+                  ps->set_property ("tight-spacing", SCM_BOOL_T);
+                }
+              system_specs = scm_cons (ps->self_scm (), system_specs);
+              ps->unprotect ();
+
+              if (scm_is_pair (labels))
+                {
+                  set_labels (scm_car (system_specs), labels);
+                  labels = SCM_EOL;
+                }
+              // FIXME: figure out penalty.
+              //set_system_penalty (ps, scores_[i].header_);
+            }
+          /* Set properties to avoid widowed/orphaned lines.
+             Single-line markup_lists are excluded, but in future
+             we may want to add the case of a very short, single line. */
+          if (first && last)
+            {
+              last->set_property ("last-markup-line", SCM_BOOL_T);
+              first->set_property ("first-markup-line", SCM_BOOL_T);
+            }
+        }
       else
       else
-       assert (0);
+        assert (0);
     }
 
   system_specs = scm_reverse_x (system_specs, SCM_EOL);
     }
 
   system_specs = scm_reverse_x (system_specs, SCM_EOL);
@@ -353,35 +578,58 @@ Paper_book::systems ()
     return systems_;
 
   systems_ = SCM_EOL;
     return systems_;
 
   systems_ = SCM_EOL;
-  SCM specs = get_system_specs ();
-  for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
+  if (scm_is_pair (bookparts_))
     {
     {
-      if (Paper_score *pscore = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
-       {
-         SCM system_list = scm_vector_to_list (pscore->get_paper_systems ());
-         system_list = scm_reverse (system_list);
-         systems_ = scm_append (scm_list_2 (system_list, systems_));
-       }
-      else
-       {
-         systems_ = scm_cons (scm_car (s), systems_);
-       }
+      SCM system_list = SCM_EOL;
+      for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
+        if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
+          system_list = scm_cons (pbookpart->systems (), system_list);
+      systems_ = scm_append (scm_reverse_x (system_list, SCM_EOL));
     }
     }
-  
-  systems_ = scm_reverse (systems_);
-
-  int i = 0;
-  Prob *last = 0;
-  for (SCM s = systems_; scm_is_pair (s); s = scm_cdr (s))
+  else
     {
     {
-      Prob *ps = unsmob_prob (scm_car (s));
-      ps->set_property ("number", scm_from_int (++i));
-
-      if (last
-         && to_boolean (last->get_property ("is-title"))
-         && !scm_is_number (ps->get_property ("penalty")))
-       ps->set_property ("penalty", scm_from_int (10000));
-      last = ps;
+      SCM specs = get_system_specs ();
+      for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
+        {
+          if (Paper_score * pscore
+              = dynamic_cast<Paper_score *> (unsmob_music_output (scm_car (s))))
+            {
+              SCM system_list
+                = scm_vector_to_list (pscore->get_paper_systems ());
+
+              systems_ = scm_reverse_x (system_list, systems_);
+            }
+          else
+            {
+              systems_ = scm_cons (scm_car (s), systems_);
+            }
+        }
+      systems_ = scm_reverse_x (systems_, SCM_EOL);
+
+      /* backwards compatibility for the old page breaker */
+      int i = 0;
+      Prob *last = 0;
+      for (SCM s = systems_; scm_is_pair (s); s = scm_cdr (s))
+        {
+          Prob *ps = unsmob_prob (scm_car (s));
+          ps->set_property ("number", scm_from_int (++i));
+
+          if (last
+              && to_boolean (last->get_property ("is-title"))
+              && !scm_is_number (ps->get_property ("penalty")))
+            ps->set_property ("penalty", scm_from_int (10000));
+          last = ps;
+
+          if (scm_is_pair (scm_cdr (s)))
+            {
+              SCM perm = ps->get_property ("page-break-permission");
+              Prob *next = unsmob_prob (scm_cadr (s));
+              if (perm == SCM_EOL)
+                next->set_property ("penalty", scm_from_int (10001));
+              else if (perm == ly_symbol2scm ("force"))
+                next->set_property ("penalty", scm_from_int (-10001));
+            }
+        }
     }
 
   return systems_;
     }
 
   return systems_;
@@ -394,22 +642,41 @@ Paper_book::pages ()
     return pages_;
 
   pages_ = SCM_EOL;
     return pages_;
 
   pages_ = SCM_EOL;
-  SCM proc = paper_->c_variable ("page-breaking");
-  pages_ = scm_apply_0 (proc, scm_list_1(self_scm ()));
-
-  /* set systems_ from the pages */
-  if (systems_ == SCM_BOOL_F)
+  if (scm_is_pair (bookparts_))
     {
     {
-      systems_ = SCM_EOL;
-      for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p))
-       {
-         Prob *page = unsmob_prob (scm_car (p));
-         SCM systems = page->get_property ("lines");
-
-         systems_ = scm_append (scm_list_2 (systems_, systems));
-       }
+      for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
+        if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
+          pages_ = scm_append_x (scm_list_2 (pages_, pbookpart->pages ()));
+    }
+  else if (scm_is_pair (scores_))
+    {
+      SCM page_breaking = paper_->c_variable ("page-breaking");
+      pages_ = scm_apply_0 (page_breaking, scm_list_1 (self_scm ()));
+
+      // Create all the page stencils.
+      SCM page_module = scm_c_resolve_module ("scm page");
+      SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
+      page_stencil = scm_variable_ref (page_stencil);
+      for (SCM pages = pages_; scm_is_pair (pages); pages = scm_cdr (pages))
+        scm_apply_1 (page_stencil, scm_car (pages), SCM_EOL);
+
+      // Perform any user-supplied post-processing.
+      SCM post_process = paper_->c_variable ("page-post-process");
+      if (ly_is_procedure (post_process))
+        scm_apply_2 (post_process, paper_->self_scm (), pages_, SCM_EOL);
+
+      /* set systems_ from the pages */
+      if (systems_ == SCM_BOOL_F)
+        {
+          systems_ = SCM_EOL;
+          for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p))
+            {
+              Prob *page = unsmob_prob (scm_car (p));
+              SCM systems = page->get_property ("lines");
+              systems_ = scm_append (scm_list_2 (systems_, systems));
+            }
+        }
     }
     }
-
   return pages_;
 }
 
   return pages_;
 }