--- /dev/null
+\version "2.11.63"
+
+\header {
+ lsrtags = "paper-and-layout"
+ texidocs = "@code{\\bookpart} can be used to split a book into
+several parts.
+Each part last page can be affected by @code{ragged-bottom-last}.
+Header and footer markups can detect a part last page, and make the
+difference with the book last page.
+"
+ doctitle = "Book parts"
+}
+
+#(set-default-paper-size "a6")
+
+\book {
+
+ %% book paper, which is inherited by all children bookparts
+ \paper {
+ ragged-last-bottom = ##t
+ %% Page footer: add a different part-tagline at part last page
+ oddFooterMarkup = \markup {
+ \column {
+ \fill-line {
+ %% Copyright header field only on book first page.
+ \on-the-fly #first-page \fromproperty #'header:copyright
+ }
+ \fill-line {
+ %% Part tagline header field only on each part last page.
+ \on-the-fly #part-last-page \fromproperty #'header:parttagline
+ }
+ \fill-line {
+ %% Tagline header field only on book last page.
+ \on-the-fly #last-page \fromproperty #'header:tagline
+ }
+ }
+ }
+ }
+
+ %% book header, which is inherited by the first bookpart
+ \header {
+ title = "Book title"
+ copyright = "Copyright line on book first page"
+ parttagline = "Part tagline"
+ tagline = "Book tagline"
+ }
+
+ \bookpart {
+ %% a different page breaking function may be used on each part
+ \paper { #(define page-breaking optimal-page-breaks) }
+ \header { subtitle = "First part" }
+ \markup { The first book part }
+ \markup { a page break }
+ \pageBreak
+ \markup { first part last page }
+ \markup \wordwrap { with ragged-last-bottom (see the space below this text) }
+ }
+
+ \bookpart {
+ \header { subtitle = "Second part" }
+ { c' }
+ }
+
+}
--- /dev/null
+\version "2.11.63"
+
+\header {
+ texidoc = "A book can be split into several parts with different paper settings,
+using @code{\\bookpart}.
+
+Fonts are loaded into the top-level paper.
+Page labels are also collected into the top-level paper."
+}
+
+#(set-default-paper-size "a6")
+
+#(define-markup-command (roman-page-number layout props) ()
+ (let ((page-number (chain-assoc-get 'page:page-number props)))
+ (interpret-markup layout props (format #f "~@r" page-number))))
+
+\book {
+ \tocItem \markup "First part"
+ \header { title = "Book with several parts" }
+ \markup { First part }
+ \markup { with default paper settings. }
+
+ \bookpart {
+ \paper {
+ left-margin = 20\mm
+ right-margin = 20\mm
+ line-width = 65\mm
+ evenHeaderMarkup = \markup \fill-line { \roman-page-number "SECOND PART" \null }
+ oddHeaderMarkup = \markup \fill-line { \null "SECOND PART" \roman-page-number }
+ }
+ \tocItem \markup "Second part"
+ \markup \justify { Second part, with different margins and page header. }
+ { c' }
+ }
+
+ \tocItem \markup "Third part"
+ \markup { Third part }
+ \markuplines \table-of-contents
+}
return x;
}
+LY_DEFINE (ly_make_book_part, "ly:make-book-part",
+ 1, 0, 0, (SCM scores),
+ "Make a @code{\\bookpart} containing @code{\\scores}.")
+{
+ Book *book = new Book;
+ book->scores_ = scm_append (scm_list_2 (scores, book->scores_));
+
+ SCM x = book->self_scm ();
+ book->unprotect ();
+ return x;
+}
+
LY_DEFINE (ly_book_process, "ly:book-process",
4, 0, 0, (SCM book_smob,
SCM default_paper,
book->add_score (score);
return SCM_UNSPECIFIED;
}
+
+LY_DEFINE (ly_book_add_bookpart_x, "ly:book-add-bookpart!",
+ 2, 0, 0, (SCM book_smob, SCM book_part),
+ "Add @var{book_part} to @var{book-smob} book part list.")
+{
+ LY_ASSERT_SMOB (Book, book_smob, 1);
+ Book *book = unsmob_book (book_smob);
+ book->add_bookpart (book_part);
+ return SCM_UNSPECIFIED;
+}
paper_ = 0;
header_ = SCM_EOL;
scores_ = SCM_EOL;
+ bookparts_ = SCM_EOL;
input_location_ = SCM_EOL;
smobify_self ();
paper_ = 0;
header_ = SCM_EOL;
scores_ = SCM_EOL;
+ bookparts_ = SCM_EOL;
input_location_ = SCM_EOL;
smobify_self ();
t = SCM_CDRLOC (*t);
newscore->unprotect ();
}
+
+ t = &bookparts_;
+ for (SCM p = s.bookparts_; scm_is_pair (p); p = scm_cdr (p))
+ {
+ Book *newpart = unsmob_book (scm_car (p))->clone ();
+
+ *t = scm_cons (newpart->self_scm (), SCM_EOL);
+ t = SCM_CDRLOC (*t);
+ newpart->unprotect ();
+ }
}
Input *
if (book->paper_)
scm_gc_mark (book->paper_->self_scm ());
scm_gc_mark (book->scores_);
+ scm_gc_mark (book->bookparts_);
scm_gc_mark (book->input_location_);
return book->header_;
scores_ = scm_cons (s, scores_);
}
+void
+Book::set_parent (Book *parent)
+{
+ if (!paper_)
+ {
+ paper_ = new Output_def ();
+ paper_->unprotect ();
+ }
+ paper_->parent_ = parent->paper_;
+ /* If this part is the first child of parent, copy its header */
+ if (ly_is_module (parent->header_) && (scm_is_null (parent->bookparts_)))
+ {
+ SCM tmp_header = ly_make_anonymous_module (false);
+ ly_module_copy (tmp_header, parent->header_);
+ if (ly_is_module (header_))
+ ly_module_copy (tmp_header, header_);
+ header_ = tmp_header;
+ }
+}
-/* Concatenate all score outputs into a Paper_book
+/* Before an explicit \bookpart is encountered, scores are added to the book.
+ * But once a bookpart is added, the previous scores shall be collected into
+ * a new bookpart.
*/
-Paper_book *
-Book::process (Output_def *default_paper,
- Output_def *default_layout)
+void
+Book::add_scores_to_bookpart ()
+{
+ if (scm_is_pair (scores_))
+ {
+ /* If scores have been added to this book, add them to a child
+ * book part */
+ Book *part = new Book;
+ part->set_parent (this);
+ part->scores_ = scores_;
+ bookparts_ = scm_cons (part->self_scm (), bookparts_);
+ part->unprotect ();
+ scores_ = SCM_EOL;
+ }
+}
+
+void
+Book::add_bookpart (SCM b)
+{
+ add_scores_to_bookpart ();
+ Book *part = unsmob_book (b);
+ part->set_parent (this);
+ bookparts_ = scm_cons (b, bookparts_);
+}
+
+bool
+Book::error_found ()
{
for (SCM s = scores_; scm_is_pair (s); s = scm_cdr (s))
if (Score *score = unsmob_score (scm_car (s)))
if (score->error_found_)
- return 0;
+ return true;
+
+ for (SCM part = bookparts_; scm_is_pair (part); part = scm_cdr (part))
+ if (Book *bookpart = unsmob_book (scm_car (part)))
+ if (bookpart->error_found ())
+ return true;
+
+ return false;
+}
+
+Paper_book *
+Book::process (Output_def *default_paper,
+ Output_def *default_layout)
+{
+ return process (default_paper, default_layout, 0);
+}
+
+void
+Book::process_bookparts (Paper_book *output_paper_book, Output_def *paper, Output_def *layout)
+{
+ add_scores_to_bookpart ();
+ for (SCM p = scm_reverse (bookparts_); scm_is_pair (p); p = scm_cdr (p))
+ {
+ if (Book *book = unsmob_book (scm_car (p)))
+ {
+ Paper_book *paper_book_part = book->process (paper, layout, output_paper_book);
+ if (paper_book_part)
+ output_paper_book->add_bookpart (paper_book_part->self_scm ());
+ }
+ }
+}
+void
+Book::process_score (SCM s, Paper_book *output_paper_book, Output_def *layout)
+{
+ if (Score *score = unsmob_score (scm_car (s)))
+ {
+ SCM outputs = score
+ ->book_rendering (output_paper_book->paper_, layout);
+
+ while (scm_is_pair (outputs))
+ {
+ Music_output *output = unsmob_music_output (scm_car (outputs));
+
+ if (Performance *perf = dynamic_cast<Performance *> (output))
+ output_paper_book->add_performance (perf->self_scm ());
+ else if (Paper_score *pscore = dynamic_cast<Paper_score *> (output))
+ {
+ if (ly_is_module (score->get_header ()))
+ output_paper_book->add_score (score->get_header ());
+ output_paper_book->add_score (pscore->self_scm ());
+ }
+
+ outputs = scm_cdr (outputs);
+ }
+ }
+ else if (Text_interface::is_markup_list (scm_car (s))
+ || unsmob_page_marker (scm_car (s)))
+ output_paper_book->add_score (scm_car (s));
+ else
+ assert (0);
+
+}
+
+/* Concatenate all score or book part outputs into a Paper_book
+ */
+Paper_book *
+Book::process (Output_def *default_paper,
+ Output_def *default_layout,
+ Paper_book *parent_part)
+{
Output_def *paper = paper_ ? paper_ : default_paper;
+
+ /* If top book, recursively check score errors */
+ if (!parent_part && error_found ())
+ return 0;
+
if (!paper)
return 0;
-
+
Paper_book *paper_book = new Paper_book ();
Real scale = scm_to_double (paper->c_variable ("output-scale"));
Output_def *scaled_bookdef = scale_output_def (paper, scale);
-
paper_book->paper_ = scaled_bookdef;
- scaled_bookdef->unprotect ();
-
+ if (parent_part)
+ {
+ paper_book->parent_ = parent_part;
+ paper_book->paper_->parent_ = parent_part->paper_;
+ }
paper_book->header_ = header_;
- /* Render in order of parsing. */
- for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
+ if (scm_is_pair (bookparts_))
{
- if (Score *score = unsmob_score (scm_car (s)))
- {
- SCM outputs = score
- ->book_rendering (paper_book->paper_, default_layout);
-
- while (scm_is_pair (outputs))
- {
- Music_output *output = unsmob_music_output (scm_car (outputs));
-
- if (Performance *perf = dynamic_cast<Performance *> (output))
- paper_book->add_performance (perf->self_scm ());
- else if (Paper_score *pscore = dynamic_cast<Paper_score *> (output))
- {
- if (ly_is_module (score->get_header ()))
- paper_book->add_score (score->get_header ());
- paper_book->add_score (pscore->self_scm ());
- }
-
- outputs = scm_cdr (outputs);
- }
- }
- else if (Text_interface::is_markup_list (scm_car (s))
- || unsmob_page_marker (scm_car (s)))
- paper_book->add_score (scm_car (s));
- else
- assert (0);
+ /* Process children book parts */
+ process_bookparts (paper_book, paper, default_layout);
+ }
+ else
+ {
+ /* Process scores */
+ /* Render in order of parsing. */
+ for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
+ {
+ process_score (s, paper_book, default_layout);
+ }
}
return paper_book;
}
-
SCM header_;
Output_def *paper_;
SCM scores_;
+ SCM bookparts_;
SCM input_location_;
Book (Book const &);
VIRTUAL_COPY_CONSTRUCTOR(Book, Book);
Book ();
void add_score (SCM);
+ void add_bookpart (SCM);
Paper_book *process (Output_def *def_paper,
Output_def *def_layout);
+ Paper_book *process (Output_def *default_paper,
+ Output_def *default_layout,
+ Paper_book *parent_part);
void set_keys ();
+
+protected:
+ void set_parent (Book *parent);
+ void add_scores_to_bookpart ();
+ bool error_found ();
+ void process_score (SCM score,
+ Paper_book *output_paper_book,
+ Output_def *layout);
+ void process_bookparts (Paper_book *output_paper_book,
+ Output_def *paper,
+ Output_def *layout);
};
DECLARE_UNSMOB (Book, book);
Output_def *get_layout (Lily_parser *parser);
Output_def *get_midi (Lily_parser *parser);
Output_def *get_paper (Lily_parser *parser);
+void stack_paper (Lily_parser *parser, Output_def *paper);
+void unstack_paper (Lily_parser *parser);
+void set_paper (Lily_parser *parser, Output_def *paper);
SCM get_header (Lily_parser *parser);
#endif /* LILY_PARSER_HH */
SCM header_;
SCM header_0_;
SCM scores_;
+ SCM bookparts_;
+ Paper_book *parent_;
Output_def *paper_;
Paper_book ();
+ Output_def *top_paper ();
+
void add_score (SCM);
+ void add_bookpart (SCM);
void add_performance (SCM);
SCM performances () const;
void classic_output (SCM output_channel);
void output (SCM output_channel);
+
+protected:
+ void classic_output_aux (SCM output,
+ int *first_performance_number);
+ void output_aux (SCM output_channel,
+ bool is_last,
+ int *first_page_number,
+ int *first_performance_number);
};
DECLARE_UNSMOB (Paper_book, paper_book)
{"alias", ALIAS},
{"alternative", ALTERNATIVE},
{"book", BOOK},
+ {"bookpart", BOOKPART},
{"change", CHANGE},
{"chordmode", CHORDMODE},
{"chords", CHORDS},
return layout;
}
+/* Return a copy of the top of $papers stack, or $defaultpaper if the
+ * stack is empty */
Output_def *
get_paper (Lily_parser *parser)
{
- SCM id = parser->lexer_->lookup_identifier ("$defaultpaper");
- Output_def *layout = unsmob_output_def (id);
+ SCM papers = parser->lexer_->lookup_identifier ("$papers");
+ Output_def *layout = ((papers == SCM_UNDEFINED) || scm_is_null (papers)) ?
+ 0 : unsmob_output_def (scm_car (papers));
+ SCM default_paper = parser->lexer_->lookup_identifier ("$defaultpaper");
+ layout = layout ? layout : unsmob_output_def (default_paper);
layout = layout ? dynamic_cast<Output_def *> (layout->clone ()) : new Output_def;
layout->set_variable (ly_symbol2scm ("is-paper"), SCM_BOOL_T);
return layout;
}
+/* Stack a paper on top of $papers */
+void
+stack_paper (Lily_parser *parser, Output_def *paper)
+{
+ parser->lexer_->set_identifier (ly_symbol2scm ("$papers"),
+ scm_cons (paper->self_scm (),
+ parser->lexer_->lookup_identifier ("$papers")));
+}
+
+/* Unstack a paper from $papers */
+void
+unstack_paper (Lily_parser *parser)
+{
+ parser->lexer_->set_identifier (ly_symbol2scm ("$papers"),
+ scm_cdr (parser->lexer_->lookup_identifier ("$papers")));
+}
+
+/* Change the paper on top of $papers */
+void
+set_paper (Lily_parser *parser, Output_def *paper)
+{
+ scm_set_car_x (parser->lexer_->lookup_identifier ("$papers"), paper->self_scm ());
+}
+
SCM
get_header (Lily_parser *parser)
{
Real
Page_breaking::page_height (int page_num, bool last) const
{
+ bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
SCM mod = scm_c_resolve_module ("scm page");
SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
SCM make_page = scm_c_module_lookup (mod, "make-page");
SCM page = scm_apply_0 (make_page, scm_list_n (
book_->self_scm (),
ly_symbol2scm ("page-number"), scm_from_int (page_num),
- ly_symbol2scm ("is-last"), scm_from_bool (last),
+ ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
+ ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
SCM_UNDEFINED));
SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
return scm_to_double (height) - page_top_space_;
SCM book = book_->self_scm ();
int first_page_number
= robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
+ bool last_bookpart = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
SCM ret = SCM_EOL;
- SCM label_page_table = SCM_EOL;
+ SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
+ if (label_page_table == SCM_UNDEFINED)
+ label_page_table = SCM_EOL;
for (vsize i = 0; i < lines_per_page.size (); i++)
{
SCM page_num = scm_from_int (i + first_page_number);
- SCM last = scm_from_bool (i == lines_per_page.size () - 1);
- SCM rag = scm_from_bool (ragged () || (to_boolean (last)
- && ragged_last ()));
+ bool partbook_last_page = (i == lines_per_page.size () - 1);
+ SCM rag = scm_from_bool (ragged () || ( partbook_last_page && ragged_last ()));
SCM line_count = scm_from_int (lines_per_page[i]);
SCM lines = scm_list_head (systems, line_count);
SCM page = scm_apply_0 (make_page,
- scm_list_n (book, lines, page_num,
- rag, last, SCM_UNDEFINED));
-
+ scm_list_n (book, lines, page_num, rag,
+ scm_from_bool (last_bookpart),
+ scm_from_bool (partbook_last_page),
+ SCM_UNDEFINED));
/* collect labels */
for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
{
ret = scm_cons (page, ret);
systems = scm_list_tail (systems, line_count);
}
- book_->paper_->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
+ book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
ret = scm_reverse (ret);
return ret;
}
LY_DEFINE (ly_paper_book_scopes, "ly:paper-book-scopes",
1, 0, 0, (SCM book),
- "Return pages in layout book @var{book}.")
+ "Return scopes in layout book @var{book}.")
{
LY_ASSERT_SMOB (Paper_book, book, 1);
Paper_book *pb = unsmob_paper_book (book);
SCM scopes = SCM_EOL;
+ if (pb->parent_)
+ {
+ scopes = ly_paper_book_scopes (pb->parent_->self_scm ());
+ }
if (ly_is_module (pb->header_))
scopes = scm_cons (pb->header_, scopes);
header_0_ = SCM_EOL;
pages_ = SCM_BOOL_F;
scores_ = SCM_EOL;
+ bookparts_ = SCM_EOL;
performances_ = SCM_EOL;
systems_ = SCM_BOOL_F;
paper_ = 0;
+ parent_ = 0;
smobify_self ();
}
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->bookparts_);
return b->systems_;
}
return 1;
}
+Output_def *
+Paper_book::top_paper ()
+{
+ Output_def *paper = paper_;
+ while (paper->parent_)
+ paper = paper->parent_;
+ return paper;
+}
+
SCM
dump_fields ()
{
scores_ = scm_cons (s, scores_);
}
+void
+Paper_book::add_bookpart (SCM p)
+{
+ bookparts_ = scm_cons (p, bookparts_);
+}
+
void
Paper_book::add_performance (SCM s)
{
}
void
-Paper_book::output (SCM output_channel)
+Paper_book::output_aux (SCM output_channel,
+ bool is_last,
+ int *first_page_number,
+ int *first_performance_number)
{
if (scm_is_pair (performances_))
{
SCM proc = ly_lily_module_constant ("write-performances-midis");
- scm_call_2 (proc, performances (), output_channel);
+ scm_call_3 (proc,
+ performances (),
+ output_channel,
+ scm_long2num (*first_performance_number));
+ *first_performance_number += scm_ilength (performances_);
}
- if (scores_ == SCM_EOL)
- return;
+ if (scm_is_pair (bookparts_))
+ {
+ for (SCM p = scm_reverse (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)));
+ pbookpart->output_aux (output_channel,
+ is_last_part,
+ first_page_number,
+ first_performance_number);
+ }
+ }
+ else
+ {
+ if (scores_ == SCM_EOL)
+ return;
+ paper_->set_variable (ly_symbol2scm ("first-page-number"),
+ scm_long2num (*first_page_number));
+ paper_->set_variable (ly_symbol2scm ("is-last-bookpart"),
+ ly_bool2scm (is_last));
+ /* Generate all stencils to trigger font loads. */
+ *first_page_number += scm_ilength (pages ());
+ }
+}
- /* Generate all stencils to trigger font loads. */
- pages ();
+void
+Paper_book::output (SCM output_channel)
+{
+ int first_page_number = robust_scm2int (paper_->c_variable ("first-page-number"), 1);
+ int first_performance_number = 0;
+ output_aux (output_channel,
+ true,
+ &first_page_number,
+ &first_performance_number);
SCM scopes = SCM_EOL;
if (ly_is_module (header_))
}
void
-Paper_book::classic_output (SCM output)
+Paper_book::classic_output_aux (SCM output,
+ int *first_performance_number)
{
if (scm_is_pair (performances_))
{
SCM proc = ly_lily_module_constant ("write-performances-midis");
-
- scm_call_2 (proc, performances (), output);
+ scm_call_3 (proc,
+ performances (),
+ output,
+ scm_long2num (*first_performance_number));
+ *first_performance_number += scm_ilength (performances_);
}
/* Generate all stencils to trigger font loads. */
systems ();
+}
+
+void
+Paper_book::classic_output (SCM output)
+{
+ int first_performance_number = 0;
+ classic_output_aux (output, &first_performance_number);
SCM scopes = SCM_EOL;
if (ly_is_module (header_))
SCM props = paper_->lookup_variable (ly_symbol2scm ("book-title-properties"));
Prob *ps = make_paper_system (props);
paper_system_set_stencil (ps, title);
-
+
system_specs = scm_cons (ps->self_scm (), system_specs);
ps->unprotect ();
}
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_);
- }
+ for (SCM p = scm_reverse (bookparts_); scm_is_pair (p); p = scm_cdr (p))
+ if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
+ systems_ = scm_append_x (scm_list_2 (systems_, pbookpart->systems ()));
}
-
- systems_ = scm_reverse (systems_);
-
- /* 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))
+ 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 ());
+ system_list = scm_reverse (system_list);
+ systems_ = scm_append (scm_list_2 (system_list, systems_));
+ }
+ else
+ {
+ systems_ = scm_cons (scm_car (s), systems_);
+ }
+ }
+ systems_ = scm_reverse (systems_);
- if (scm_is_pair (scm_cdr (s)))
+ /* 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))
{
- 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));
+ 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 pages_;
pages_ = SCM_EOL;
- SCM proc = paper_->c_variable ("page-breaking-wrapper");
- 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");
+ for (SCM p = scm_reverse (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
+ {
+ SCM proc = paper_->c_variable ("page-breaking-wrapper");
+ pages_ = scm_apply_0 (proc, scm_list_1 (self_scm ()));
- systems_ = scm_append (scm_list_2 (systems_, systems));
+ /* 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_;
}
%token ALIAS "\\alias"
%token ALTERNATIVE "\\alternative"
%token BOOK "\\book"
+%token BOOKPART "\\bookpart"
%token CHANGE "\\change"
%token CHORDMODE "\\chordmode"
%token CHORDS "\\chords"
%type <book> book_block
%type <book> book_body
+%type <book> bookpart_block
+%type <book> bookpart_body
%type <i> bare_unsigned
%type <scm> figured_bass_alteration
scm_call_2 (proc, PARSER->self_scm (), book->self_scm ());
book->unprotect ();
}
+ | bookpart_block {
+ Book *bookpart = $1;
+ SCM proc = PARSER->lexer_->lookup_identifier ("toplevel-bookpart-handler");
+ scm_call_2 (proc, PARSER->self_scm (), bookpart->self_scm ());
+ bookpart->unprotect ();
+ }
| score_block {
Score *score = $1;
$$ = $1->self_scm ();
$1->unprotect ();
}
+ | bookpart_block {
+ $$ = $1->self_scm ();
+ $1->unprotect ();
+ }
| output_def {
$$ = $1->self_scm ();
$1->unprotect ();
book_block:
BOOK '{' book_body '}' {
$$ = $3;
+ unstack_paper (PARSER);
}
;
$$->origin ()->set_spot (@$);
$$->paper_ = dynamic_cast<Output_def*> (unsmob_output_def (PARSER->lexer_->lookup_identifier ("$defaultpaper"))->clone ());
$$->paper_->unprotect ();
+ stack_paper (PARSER, $$->paper_);
$$->header_ = PARSER->lexer_->lookup_identifier ("$defaultheader");
}
| BOOK_IDENTIFIER {
| book_body paper_block {
$$->paper_ = $2;
$2->unprotect ();
+ set_paper (PARSER, $2);
+ }
+ | book_body bookpart_block {
+ Book *bookpart = $2;
+ SCM proc = PARSER->lexer_->lookup_identifier ("book-bookpart-handler");
+ scm_call_2 (proc, $$->self_scm (), bookpart->self_scm ());
+ bookpart->unprotect ();
}
| book_body score_block {
Score *score = $2;
| book_body error {
$$->paper_ = 0;
$$->scores_ = SCM_EOL;
+ $$->bookparts_ = SCM_EOL;
}
| book_body object_id_setting {
$$->user_key_ = ly_scm2string ($2);
}
;
+bookpart_block:
+ BOOKPART '{' bookpart_body '}' {
+ $$ = $3;
+ }
+ ;
+
+bookpart_body:
+ {
+ $$ = new Book;
+ $$->origin ()->set_spot (@$);
+ }
+ | BOOK_IDENTIFIER {
+ $$ = unsmob_book ($1);
+ $$->protect ();
+ $$->origin ()->set_spot (@$);
+ }
+ | bookpart_body paper_block {
+ $$->paper_ = $2;
+ $2->unprotect ();
+ }
+ | bookpart_body score_block {
+ Score *score = $2;
+ SCM proc = PARSER->lexer_->lookup_identifier ("bookpart-score-handler");
+ scm_call_2 (proc, $$->self_scm (), score->self_scm ());
+ score->unprotect ();
+ }
+ | bookpart_body composite_music {
+ Music *music = unsmob_music ($2);
+ SCM proc = PARSER->lexer_->lookup_identifier ("bookpart-music-handler");
+ scm_call_3 (proc, PARSER->self_scm (), $$->self_scm (), music->self_scm ());
+ }
+ | bookpart_body full_markup {
+ SCM proc = PARSER->lexer_->lookup_identifier ("bookpart-text-handler");
+ scm_call_2 (proc, $$->self_scm (), scm_list_1 ($2));
+ }
+ | bookpart_body full_markup_list {
+ SCM proc = PARSER->lexer_->lookup_identifier ("bookpart-text-handler");
+ scm_call_2 (proc, $$->self_scm (), $2);
+ }
+ | bookpart_body lilypond_header {
+ $$->header_ = $2;
+ }
+ | bookpart_body error {
+ $$->paper_ = 0;
+ $$->scores_ = SCM_EOL;
+ }
+ | bookpart_body object_id_setting {
+ $$->user_key_ = ly_scm2string ($2);
+ }
+ ;
+
score_block:
SCORE '{' score_body '}' {
$$ = $3;
#(define musicQuotes (make-hash-table 29))
#(define toplevel-book-handler print-book-with-defaults)
+#(define toplevel-bookpart-handler collect-bookpart-for-book)
#(define toplevel-music-handler collect-music-for-book)
#(define toplevel-score-handler collect-scores-for-book)
#(define toplevel-text-handler collect-scores-for-book)
+#(define book-bookpart-handler ly:book-add-bookpart!)
#(define book-music-handler collect-book-music-for-book)
#(define book-score-handler ly:book-add-score!)
#(define book-text-handler ly:book-add-score!)
+#(define bookpart-score-handler ly:book-add-score!)
+#(define bookpart-text-handler ly:book-add-score!)
+#(define bookpart-music-handler collect-book-music-for-book)
\include "predefined-fretboards-init.ly"
#(ly:set-option 'old-relative #f)
-#(define toplevel-scores '())
+#(define toplevel-scores (list))
+#(define toplevel-bookparts (list))
#(define output-count 0)
#(define $defaultheader #f)
#(define version-seen #f)
(version-not-seen-message input-file-name))
#(ly:set-option 'protected-scheme-parsing #f)
-#(if (or (pair? toplevel-scores) output-empty-score-list)
- ((if (defined? 'default-toplevel-book-handler)
- default-toplevel-book-handler
- toplevel-book-handler)
- parser
- (apply ly:make-book $defaultpaper $defaultheader toplevel-scores)))
+#(let ((book-handler (if (defined? 'default-toplevel-book-handler)
+ default-toplevel-book-handler
+ toplevel-book-handler)))
+ (cond ((pair? toplevel-bookparts)
+ (let ((book (ly:make-book $defaultpaper $defaultheader)))
+ (map (lambda (part)
+ (ly:book-add-bookpart! book part))
+ (reverse! toplevel-bookparts))
+ (set! toplevel-bookparts (list))
+ ;; if scores have been defined after the last explicit \bookpart:
+ (if (pair? toplevel-scores)
+ (map (lambda (score)
+ (ly:book-add-score! book score))
+ (reverse! toplevel-scores)))
+ (set! toplevel-scores (list))
+ (book-handler parser book)))
+ ((or (pair? toplevel-scores) output-empty-score-list)
+ (book-handler parser (apply ly:make-book $defaultpaper
+ $defaultheader toplevel-scores)))))
#(if (eq? expect-error (ly:parser-has-error? parser))
(ly:parser-clear-error parser)
}
}
+%% Book first page and last page predicates
#(define (first-page layout props arg)
+ (define (ancestor layout)
+ "Return the topmost layout ancestor"
+ (let ((parent (ly:output-def-parent layout)))
+ (if (not (ly:output-def? parent))
+ layout
+ (ancestor parent))))
(if (= (chain-assoc-get 'page:page-number props -1)
- (ly:output-def-lookup layout 'first-page-number))
+ (ly:output-def-lookup (ancestor layout) 'first-page-number))
(interpret-markup layout props arg)
empty-stencil))
#(define (last-page layout props arg)
- (if (chain-assoc-get 'page:last? props #f)
- (interpret-markup layout props arg)
- empty-stencil))
+ (if (and (chain-assoc-get 'page:is-bookpart-last-page props #f)
+ (chain-assoc-get 'page:is-last-bookpart props #f))
+ (interpret-markup layout props arg)
+ empty-stencil))
#(define (not-first-page layout props arg)
+ (define (ancestor layout)
+ "Return the topmost layout ancestor"
+ (let ((parent (ly:output-def-parent layout)))
+ (if (not (ly:output-def? parent))
+ layout
+ (ancestor parent))))
(if (not (= (chain-assoc-get 'page:page-number props -1)
- (ly:output-def-lookup layout 'first-page-number)))
- (interpret-markup layout props arg)
- empty-stencil))
+ (ly:output-def-lookup (ancestor layout) 'first-page-number)))
+ (interpret-markup layout props arg)
+ empty-stencil))
+
+%% Bookpart first page and last page predicates
+#(define (part-first-page layout props arg)
+ (if (= (chain-assoc-get 'page:page-number props -1)
+ (ly:output-def-lookup layout 'first-page-number))
+ (interpret-markup layout props arg)
+ empty-stencil))
+
+#(define (part-last-page layout props arg)
+ (if (chain-assoc-get 'page:is-bookpart-last-page props #f)
+ (interpret-markup layout props arg)
+ empty-stencil))
%% unused
#(define (not-single-page layout props arg)
(= (interval-start system-extent)
(interval-end system-extent))))))
-(define (stretch-and-draw-page paper-book systems page-number ragged last)
+(define (stretch-and-draw-page paper-book systems page-number ragged
+ is-last-bookpart is-bookpart-last-page)
(define (max-stretch sys)
(if (ly:grob? sys)
(ly:grob-property sys 'max-stretch)
(let* ((page (make-page paper-book
'page-number page-number
- 'is-last last))
+ 'is-last-bookpart is-last-bookpart
+ 'is-bookpart-last-page is-bookpart-last-page))
(paper (ly:paper-book-paper paper-book))
(height (page-printable-height page))
; there is a certain amount of impreciseness going on here:
inter-system-space))
user)))
-(define (walk-paths done-lines best-paths current-lines last current-best
- paper-book page-alist)
+(define (walk-paths done-lines best-paths current-lines is-last-bookpart
+ is-bookpart-last-page current-best paper-book page-alist)
"Return the best optimal-page-break-node that contains
CURRENT-LINES. DONE-LINES.reversed ++ CURRENT-LINES is a consecutive
ascending range of lines, and BEST-PATHS contains the optimal breaks
(let* ((paper (ly:paper-book-paper paper-book))
(this-page (make-page
paper-book
- 'is-last last
+ 'is-last-bookpart is-last-bookpart
+ 'is-bookpart-last-page is-bookpart-last-page
'page-number (if (null? best-paths)
(ly:output-def-lookup paper 'first-page-number)
(1+ (page-page-number (first best-paths))))))
(ragged-all (eq? #t (ly:output-def-lookup paper 'ragged-bottom)))
(ragged-last (eq? #t (ly:output-def-lookup paper 'ragged-last-bottom)))
- (ragged (or ragged-all (and ragged-last last)))
+ (ragged (or ragged-all (and ragged-last is-bookpart-last-page)))
(space-to-fill (page-maximum-space-to-fill this-page current-lines paper))
(vertical-spacing (space-systems space-to-fill current-lines ragged paper #f))
(satisfied-constraints (car vertical-spacing))
(force (if satisfied-constraints
- (if (and last ragged-last)
+ (if (and is-bookpart-last-page ragged-last)
0.0
satisfied-constraints)
10000))
(list
"\nuser pen " user-penalty
"\nsatisfied-constraints" satisfied-constraints
- "\nlast? " last "ragged?" ragged
+ "\nlast? " is-bookpart-last-page "ragged?" ragged
"\nis-better " is-better " total-penalty " total-penalty "\n"
"\nconfig " positions
"\nforce " force
satisfied-constraints)
(walk-paths (cdr done-lines) (cdr best-paths)
(cons (car done-lines) current-lines)
- last new-best
+ is-last-bookpart is-bookpart-last-page new-best
paper-book page-alist)
new-best)))
-(define (walk-lines done best-paths todo paper-book page-alist)
+(define (walk-lines done best-paths todo paper-book page-alist is-last-bookpart)
"Return the best page breaking as a single
page node for optimally breaking TODO ++
DONE.reversed. BEST-PATHS is a list of break nodes corresponding to
(if (null? todo)
(car best-paths)
(let* ((this-line (car todo))
- (last (null? (cdr todo)))
- (next (walk-paths done best-paths (list this-line) last #f
- paper-book page-alist)))
+ (is-bookpart-last-page (null? (cdr todo)))
+ (next (walk-paths done best-paths (list this-line) is-last-bookpart
+ is-bookpart-last-page #f paper-book page-alist)))
(walk-lines (cons this-line done)
(cons next best-paths)
(cdr todo)
paper-book
- page-alist))))
+ page-alist
+ is-last-bookpart))))
(define-public (optimal-page-breaks paper-book)
"Return pages as a list starting with 1st page. Each page is a 'page Prob."
(lines (ly:paper-book-systems paper-book))
(page-alist (layout->page-init paper))
(force-equalization-factor (ly:output-def-lookup
- paper 'verticalequalizationfactor 0.3)))
+ paper 'verticalequalizationfactor 0.3))
+ (is-last-bookpart (ly:output-def-lookup paper 'is-last-bookpart)))
(ly:message (_ "Calculating page breaks..."))
- (let* ((best-break-node (walk-lines '() '() lines paper-book page-alist))
+ (let* ((best-break-node (walk-lines '() '() lines paper-book page-alist is-last-bookpart))
(break-nodes (get-path best-break-node '())))
- (page-set-property! (car (last-pair break-nodes)) 'is-last #t)
(if #f; (ly:get-option 'verbose)
(begin
(display (list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; parser <-> output hooks.
-
+(define-public (collect-bookpart-for-book parser book-part)
+ "Toplevel book-part handler"
+ (define (add-bookpart book-part)
+ (ly:parser-define!
+ parser 'toplevel-bookparts
+ (cons book-part (ly:parser-lookup parser 'toplevel-bookparts))))
+ ;; If toplevel scores have been found before this \bookpart,
+ ;; add them first to a dedicated bookpart
+ (if (pair? (ly:parser-lookup parser 'toplevel-scores))
+ (begin
+ (add-bookpart (ly:make-book-part
+ (ly:parser-lookup parser 'toplevel-scores)))
+ (ly:parser-define! parser 'toplevel-scores (list))))
+ (add-bookpart book-part))
+
(define-public (collect-scores-for-book parser score)
(ly:parser-define!
parser 'toplevel-scores
(cons score (ly:parser-lookup parser 'toplevel-scores))))
-(define (collect-music-aux score-handler parser music)
+(define-public (collect-music-aux score-handler parser music)
(define (music-property symbol)
(let ((value (ly:music-property music symbol)))
(if (not (null? value))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
-(define-public (write-performances-midis performances basename)
+(define-public (write-performances-midis performances basename . rest)
(let ((midi-ext (ly:get-option 'midi-extension)))
(let
loop
((perfs performances)
- (count 0))
-
-
+ (count (if (null? rest) 0 (car rest))))
(if (pair? perfs)
(begin
(ly:performance-write
\f
-(define (page-headfoot layout scopes number
- sym separation-symbol dir last?)
+(define (page-headfoot layout scopes number sym separation-symbol dir
+ is-last-bookpart is-bookpart-last-page)
"Create a stencil including separating space."
(stencil (ly:make-stencil "" '(0 . 0) '(0 . 0)))
(head-stencil
(if (procedure? header-proc)
- (header-proc layout scopes number last?)
- #f))
- )
+ (header-proc layout scopes number is-last-bookpart is-bookpart-last-page)
+ #f)))
(if (and (number? sep)
(ly:stencil? head-stencil)
(layout (ly:paper-book-paper paper-book))
(scopes (ly:paper-book-scopes paper-book))
(number (page-page-number page))
- (last? (page-property page 'is-last))
- )
+ (is-last-bookpart (page-property page 'is-last-bookpart))
+ (is-bookpart-last-page (page-property page 'is-bookpart-last-page)))
(page-headfoot layout scopes number
(if (= dir UP)
(if (= dir UP)
'head-separation
'foot-separation)
- dir last?)))
+ dir is-last-bookpart is-bookpart-last-page)))
(define (page-header page)
(page-header-or-footer page UP))
;;;;;;;;;;;;;;;;;;
-(define-public ((marked-up-headfoot what-odd what-even) layout scopes page-number last?)
+(define-public ((marked-up-headfoot what-odd what-even)
+ layout scopes page-number is-last-bookpart is-bookpart-last-page)
"Read variables WHAT-ODD, WHAT-EVEN from LAYOUT, and interpret them
as markup. The PROPS argument will include variables set in SCOPES and
-page:last?, page:page-number-string and page:page-number
-"
+page:is-bookpart-last-page, page:is-last-bookpart, page:page-number-string
+and page:page-number"
(define (get sym)
(ly:output-def-lookup layout sym))
(cons 'header:tagline
(ly:modules-lookup scopes 'tagline
(ly:output-def-lookup layout 'tagline)))
- (cons 'page:last? last?)
+ (cons 'page:is-last-bookpart is-last-bookpart)
+ (cons 'page:is-bookpart-last-page is-bookpart-last-page)
(cons 'page:page-number-string
(number->string page-number))
(cons 'page:page-number page-number)))
(list pgnum-alist)
prefixed-alists
(layout-extract-page-properties layout))))
-
(interpret-markup layout props potential-markup))
empty-stencil))