X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fpaper-book.cc;h=c17ed576f1895c0a91febb29164ea9987dda1b0f;hb=e00d0d078eece6de5cbcf6b83b43238a010628c0;hp=8620970ab161ab3436df0fb05196ffcca7a8bca5;hpb=d7b187976cff5a17b795c150d4703ded396b17a0;p=lilypond.git diff --git a/lily/paper-book.cc b/lily/paper-book.cc index 8620970ab1..c17ed576f1 100644 --- a/lily/paper-book.cc +++ b/lily/paper-book.cc @@ -1,488 +1,669 @@ /* - 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--2015 Jan Nieuwenhuizen - (c) 2004 Jan Nieuwenhuizen + 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 . */ -#include -#include +#include "paper-book.hh" -#include "ly-module.hh" +#include "grob.hh" +#include "international.hh" #include "main.hh" -#include "paper-book.hh" -#include "paper-def.hh" -#include "paper-outputter.hh" -#include "paper-line.hh" +#include "output-def.hh" +#include "paper-column.hh" #include "paper-score.hh" -#include "stencil.hh" +#include "paper-system.hh" +#include "text-interface.hh" +#include "warn.hh" +#include "program-option.hh" +#include "page-marker.hh" +#include "ly-module.hh" +#include "lily-imports.hh" -static Real const MIN_COVERAGE = 0.66; -static SCM -stencil2line (Stencil* stil, bool is_title = false) +Paper_book::Paper_book () { - static SCM z; - if (!z) - z = scm_permanent_object (ly_offset2scm (Offset (0, 0))); - Offset dim = Offset (stil->extent (X_AXIS).length (), - stil->extent (Y_AXIS).length ()); - Paper_line *pl = new Paper_line (dim, scm_cons (stil->smobbed_copy (), - SCM_EOL), - -10001 * is_title, is_title); - - return scm_gc_unprotect_object (pl->self_scm ()); + header_ = 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; + parent_ = 0; + smobify_self (); } -/* Simplistic page interface */ -class Page -{ -public: - Paper_def *paper_; - static int page_count_; - int number_; - int line_count_; - - Protected_scm lines_; - Protected_scm header_; - Protected_scm footer_; - Protected_scm copyright_; - Protected_scm tagline_; - - Stencil *get_header () { return unsmob_stencil (header_); } - Stencil *get_copyright () { return unsmob_stencil (copyright_); } - Stencil *get_tagline () { return unsmob_stencil (tagline_); } - Stencil *get_footer () { return unsmob_stencil (footer_); } - - /* actual height filled with text. */ - Real height_; - - // HMMM all this size stuff to paper/paper-outputter? - Real hsize_; - Real vsize_; - Real left_margin_; - Real top_margin_; - Real bottom_margin_; - Real foot_sep_; - Real head_sep_; - Real text_width_; - - /* available area for text. */ - Real text_height (); - - Page (Paper_def*, int); - void output (Paper_outputter*, bool); -}; - -int Page::page_count_ = 0; - -Page::Page (Paper_def *paper, int number) +Paper_book::~Paper_book () { - paper_ = paper; - number_ = number; - page_count_++; - - height_ = 0; - lines_ = SCM_EOL; - line_count_ = 0; - - hsize_ = paper->get_dimension (ly_symbol2scm ("hsize")); - vsize_ = paper->get_dimension (ly_symbol2scm ("vsize")); - top_margin_ = paper->get_dimension (ly_symbol2scm ("top-margin")); - bottom_margin_ = paper->get_dimension (ly_symbol2scm ("bottom-margin")); - head_sep_ = paper->get_dimension (ly_symbol2scm ("head-sep")); - foot_sep_ = paper->get_dimension (ly_symbol2scm ("foot-sep")); - text_width_ = paper->get_dimension (ly_symbol2scm ("linewidth")); - left_margin_ = (hsize_ - text_width_) / 2; - - copyright_ = SCM_EOL; - tagline_ = SCM_EOL; - - SCM make_header = ly_scheme_function ("make-header"); - SCM make_footer = ly_scheme_function ("make-footer"); - - header_ = scm_call_2 (make_header, paper_->self_scm (), - scm_int2num (number_)); - if (get_header ()) - get_header ()->align_to (Y_AXIS, UP); - - footer_ = scm_call_2 (make_footer, paper_->self_scm (), - scm_int2num (number_)); - if (get_footer ()) - get_footer ()->align_to (Y_AXIS, UP); } -void -Page::output (Paper_outputter *out, bool is_last) +const char * const Paper_book::type_p_name_ = "ly:paper-book?"; + +SCM +Paper_book::mark_smob () const { - progress_indication ("[" + to_string (number_)); - out->output_scheme (scm_list_1 (ly_symbol2scm ("start-page"))); - Offset o (left_margin_, top_margin_); - Real vfill = line_count_ > 1 ? (text_height () - height_) / (line_count_ - 1) - : 0; - - Real coverage = height_ / text_height (); - if (coverage < MIN_COVERAGE) - /* Do not space out a badly filled page. This is too simplistic - (ie broken), because this should not vary too much between - (subsequent?) pages in a book. */ - vfill = 0; - - if (get_header ()) - { - out->output_line (stencil2line (get_header ()), &o, false); - o[Y_AXIS] += head_sep_; - } - for (SCM s = lines_; s != SCM_EOL; s = ly_cdr (s)) - { - SCM line = ly_car (s); - out->output_line (line, &o, - is_last && ly_cdr (s) != SCM_EOL && !get_copyright () - && !get_tagline () && !get_footer ()); - - /* Do not put vfill between title and its music, */ - if (scm_pair_p (ly_cdr (s)) - && (!unsmob_paper_line (line)->is_title () || vfill < 0)) - o[Y_AXIS] += vfill; - /* rather put extra just before the title. */ - if (ly_cdr (s) != SCM_EOL - && (unsmob_paper_line (ly_cadr (s))->is_title () && vfill > 0)) - o[Y_AXIS] += vfill; - } + if (paper_) + scm_gc_mark (paper_->self_scm ()); + if (parent_) + scm_gc_mark (parent_->self_scm ()); + scm_gc_mark (header_); + scm_gc_mark (header_0_); + scm_gc_mark (pages_); + scm_gc_mark (performances_); + scm_gc_mark (scores_); + scm_gc_mark (bookparts_); + return systems_; +} - o[Y_AXIS] = vsize_ - bottom_margin_; - if (get_copyright ()) - o[Y_AXIS] -= get_copyright ()->extent (Y_AXIS).length (); - if (get_tagline ()) - o[Y_AXIS] -= get_tagline ()->extent (Y_AXIS).length (); - if (get_footer ()) - o[Y_AXIS] -= get_footer ()->extent (Y_AXIS).length (); - - if (get_copyright ()) - out->output_line (stencil2line (get_copyright ()), &o, - is_last && !get_tagline () && !get_footer ()); - if (get_tagline ()) - out->output_line (stencil2line (get_tagline ()), &o, - is_last && !get_footer ()); - if (get_footer ()) - out->output_line (stencil2line (get_footer ()), &o, is_last); - out->output_scheme (scm_list_2 (ly_symbol2scm ("stop-page"), - ly_bool2scm (is_last && !get_footer ()))); - progress_indication ("]"); +Output_def * +Paper_book::top_paper () +{ + Output_def *paper = paper_; + while (paper->parent_) + paper = paper->parent_; + return paper; } -Real -Page::text_height () +SCM +dump_fields () { - Real h = vsize_ - top_margin_ - bottom_margin_; - if (get_header ()) - h -= get_header ()->extent (Y_AXIS).length () + head_sep_; - if (get_copyright () || get_tagline () || get_footer ()) - h -= foot_sep_; - if (get_copyright ()) - h -= get_copyright ()->extent (Y_AXIS).length (); - if (get_tagline ()) - h -= get_tagline ()->extent (Y_AXIS).length (); - if (get_footer ()) - h -= get_footer ()->extent (Y_AXIS).length (); - return h; + SCM fields = SCM_EOL; + for (vsize i = dump_header_fieldnames_global.size (); i--;) + fields + = scm_cons (ly_symbol2scm (dump_header_fieldnames_global[i].c_str ()), + fields); + return fields; } -/****************************************************************/ +void +Paper_book::add_score (SCM s) +{ + scores_ = scm_cons (s, scores_); +} -Paper_book::Paper_book () +void +Paper_book::add_bookpart (SCM p) { - copyright_ = SCM_EOL; - tagline_ = SCM_EOL; - - smobify_self (); + bookparts_ = scm_cons (p, bookparts_); } -Paper_book::~Paper_book () +void +Paper_book::add_performance (SCM s) { + performances_ = scm_cons (s, performances_); } -#include "ly-smobs.icc" +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_)) + { + Lily::write_performances_midis (performances (), + output_channel, + scm_from_long (*first_performance_number)); + *first_performance_number += scm_ilength (performances_); + } -IMPLEMENT_DEFAULT_EQUAL_P (Paper_book); -IMPLEMENT_SMOBS (Paper_book) -IMPLEMENT_TYPE_P (Paper_book, "ly:paper-book?") + if (scm_is_pair (bookparts_)) + { + for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p)) + if (Paper_book *pbookpart = unsmob (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 (scm_is_null (scores_)) + 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; +} -SCM -Paper_book::mark_smob (SCM smob) +void +Paper_book::output (SCM output_channel) { - Paper_book *pb = (Paper_book*) SCM_CELL_WORD_1 (smob); - for (int i = 0; i < pb->headers_.size (); i++) - scm_gc_mark (pb->headers_[i]); - for (int i = 0; i < pb->global_headers_.size (); i++) - scm_gc_mark (pb->global_headers_[i]); - for (int i = 0; i < pb->papers_.size (); i++) - scm_gc_mark (pb->papers_[i]->self_scm ()); - for (int i = 0; i < pb->scores_.size (); i++) - scm_gc_mark (pb->scores_[i]); - - scm_gc_mark (pb->copyright_); - - return pb->tagline_; + 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 (SCM_UNBNDP (paper_->c_variable ("line-width"))) + 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); + + string mod_nm = "scm framework-" + get_output_backend_name (); + + SCM mod = scm_c_resolve_module (mod_nm.c_str ()); + + if (get_program_option ("print-pages")) + { + SCM framework = ly_module_lookup (mod, + ly_symbol2scm ("output-framework")); + + if (scm_is_true (framework)) + { + SCM func = scm_variable_ref (framework); + scm_call_4 (func, + output_channel, + self_scm (), + scopes, + dump_fields ()); + } + else + warning (_f ("program option -dprint-pages not supported by backend `%s'", + get_output_backend_name ())); + } + + if (get_program_option ("preview")) + { + SCM framework + = ly_module_lookup (mod, ly_symbol2scm ("output-preview-framework")); + + if (scm_is_true (framework)) + { + SCM func = scm_variable_ref (framework); + scm_call_4 (func, + output_channel, + self_scm (), + scopes, + dump_fields ()); + } + else + warning (_f ("program option -dpreview not supported by backend `%s'", + get_output_backend_name ())); + } } -int -Paper_book::print_smob (SCM smob, SCM port, scm_print_state*) +void +Paper_book::classic_output_aux (SCM output, + long *first_performance_number) { - Paper_book *b = (Paper_book*) ly_cdr (smob); - - scm_puts ("#<", port); - scm_puts (classname (b), port); - scm_puts (" ", port); - //scm_puts (b->, port); - scm_puts (">", port); - return 1; + if (scm_is_pair (performances_)) + { + Lily::write_performances_midis (performances (), + output, + scm_from_long (*first_performance_number)); + *first_performance_number += scm_ilength (performances_); + } + + /* Generate all stencils to trigger font loads. */ + systems (); } void -Paper_book::output (String outname) +Paper_book::classic_output (SCM output) { - if (!papers_.size ()) - return; - - /* Generate all stencils to trigger font loads. */ - Link_array *pages = this->pages (); + long first_performance_number = 0; + classic_output_aux (output, &first_performance_number); + + SCM scopes = SCM_EOL; + if (ly_is_module (header_)) + scopes = scm_cons (header_, scopes); - Paper_def *paper = papers_[0]; - Paper_outputter *out = paper->get_paper_outputter (outname); - out->output_header (paper, scopes (0), pages->size (), false); + if (ly_is_module (header_0_)) + scopes = scm_cons (header_0_, scopes); - int page_count = pages->size (); - for (int i = 0; i < page_count; i++) - (*pages)[i]->output (out, i + 1 == page_count); + string format = get_output_backend_name (); + string mod_nm = "scm framework-" + format; - out->output_scheme (scm_list_1 (ly_symbol2scm ("end-output"))); + SCM mod = scm_c_resolve_module (mod_nm.c_str ()); + SCM func = scm_c_module_lookup (mod, "output-classic-framework"); - /* Ugh. */ - for (int i = pages->size (); i--;) - delete pages->elem (i); - delete pages; - + func = scm_variable_ref (func); + scm_call_4 (func, + output, + self_scm (), + scopes, + dump_fields ()); progress_indication ("\n"); } -SCM -Paper_book::scopes (int i) +/* TODO: resurrect more complex user-tweaks for titling? */ +Stencil +Paper_book::book_title () { + SCM title_func = paper_->lookup_variable (ly_symbol2scm ("book-title")); + Stencil title; + SCM scopes = SCM_EOL; - if (headers_[i]) - scopes = scm_cons (headers_[i], scopes); - if (global_headers_.size () - && global_headers_[i] && global_headers_[i] != headers_[i]) - scopes = scm_cons (global_headers_[i], scopes); - return scopes; + if (ly_is_module (header_)) + scopes = scm_cons (header_, scopes); + + SCM tit = SCM_EOL; + if (ly_is_procedure (title_func)) + tit = scm_call_2 (title_func, + paper_->self_scm (), + scopes); + + if (unsmob (tit)) + title = *unsmob (tit); + + if (!title.is_empty ()) + title.align_to (Y_AXIS, UP); + + return title; } -Stencil* -Paper_book::title (int i) +Stencil +Paper_book::score_title (SCM header) { - SCM user_title = ly_scheme_function ("user-title"); - SCM book_title = ly_scheme_function ("book-title"); - SCM score_title = ly_scheme_function ("score-title"); - SCM field = (i == 0 ? ly_symbol2scm ("bookTitle") - : ly_symbol2scm ("scoreTitle")); - - Stencil *title = 0; - SCM scopes = this->scopes (i); - SCM s = ly_modules_lookup (scopes, field); - if (s != SCM_UNDEFINED && scm_variable_bound_p (s) == SCM_BOOL_T) - title = unsmob_stencil (scm_call_2 (user_title, - papers_[0]->self_scm (), - scm_variable_ref (s))); - else - title = unsmob_stencil (scm_call_2 (i == 0 ? book_title : score_title, - papers_[0]->self_scm (), - scopes)); - if (title) - title->align_to (Y_AXIS, UP); - + SCM title_func = paper_->lookup_variable (ly_symbol2scm ("score-title")); + + Stencil title; + + SCM scopes = SCM_EOL; + if (ly_is_module (header_)) + scopes = scm_cons (header_, scopes); + + if (ly_is_module (header)) + scopes = scm_cons (header, scopes); + + SCM tit = SCM_EOL; + if (ly_is_procedure (title_func)) + tit = scm_call_2 (title_func, + paper_->self_scm (), + scopes); + + if (unsmob (tit)) + title = *unsmob (tit); + + if (!title.is_empty ()) + title.align_to (Y_AXIS, UP); + return title; } void -Paper_book::classic_output (String outname) +set_page_permission (SCM sys, SCM symbol, SCM permission) { - int count = scores_.size (); - Paper_outputter *out = papers_.top ()->get_paper_outputter (outname); - out->output_header (papers_.top (), scopes (count - 1), 0, true); - - Paper_line *first = unsmob_paper_line (scm_vector_ref (scores_.top (), - scm_int2num (0))); - Offset o (0, -0.5 * first->dim ()[Y_AXIS]); - int line_count = SCM_VECTOR_LENGTH ((SCM) scores_.top ()); - for (int i = 0; i < line_count; i++) - out->output_line (scm_vector_ref ((SCM) scores_.top (), scm_int2num (i)), - &o, i == line_count - 1); - - out->output_scheme (scm_list_1 (ly_symbol2scm ("end-output"))); - progress_indication ("\n"); + if (Paper_score *ps = unsmob (sys)) + { + vector cols = ps->get_columns (); + if (cols.size ()) + { + Paper_column *col = dynamic_cast (cols.back ()); + col->set_property (symbol, permission); + col->find_prebroken_piece (LEFT)->set_property (symbol, permission); + } + } + else if (Prob *pb = unsmob (sys)) + pb->set_property (symbol, permission); } - -/* calculate book height, #lines, stencils. */ +/* read the breakbefore property of a score block and set up the preceding + system-spec to honour it. That is, SYS should be the system spec that + immediately precedes the score (from which HEADER is taken) + in the get_system_specs () list */ void -Paper_book::init () +set_system_penalty (SCM sys, SCM header) { - int score_count = scores_.size (); - - /* Calculate the full book height. Hmm, can't we cache system - heights while making stencils? */ - height_ = 0; - for (int i = 0; i < score_count; i++) + if (ly_is_module (header)) { - Stencil *title = this->title (i); - if (title) - height_ += title->extent (Y_AXIS).length (); - - int line_count = SCM_VECTOR_LENGTH ((SCM) scores_[i]); - for (int j = 0; j < line_count; j++) - { - SCM s = scm_vector_ref ((SCM) scores_[i], scm_int2num (j)); - height_ += unsmob_paper_line (s)->dim ()[Y_AXIS]; - } + SCM force = ly_module_lookup (header, ly_symbol2scm ("breakbefore")); + if (SCM_VARIABLEP (force) + && 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); + } } +} - Paper_def *paper = papers_[0]; - SCM scopes = this->scopes (0); - - SCM make_tagline = ly_scheme_function ("make-tagline"); - tagline_ = scm_call_2 (make_tagline, paper->self_scm (), scopes); - Real tag_height = 0; - if (Stencil *s = unsmob_stencil (tagline_)) - tag_height = s->extent (Y_AXIS).length (); - height_ += tag_height; - - SCM make_copyright = ly_scheme_function ("make-copyright"); - copyright_ = scm_call_2 (make_copyright, paper->self_scm (), scopes); - Real copy_height = 0; - if (Stencil *s = unsmob_stencil (copyright_)) - copy_height = s->extent (Y_AXIS).length (); - height_ += copy_height; +void +set_labels (SCM sys, SCM labels) +{ + if (Paper_score *ps = unsmob (sys)) + { + vector cols = ps->get_columns (); + if (cols.size ()) + { + Paper_column *col = dynamic_cast (cols[0]); + col->set_property ("labels", + scm_append_x (scm_list_2 (col->get_property ("labels"), + labels))); + Paper_column *col_right + = dynamic_cast (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 (sys)) + pb->set_property ("labels", + scm_append_x (scm_list_2 (pb->get_property ("labels"), + labels))); } SCM -Paper_book::lines () +Paper_book::get_score_title (SCM header) { - int score_count = scores_.size (); - SCM lines = SCM_EOL; - for (int i = 0; i < score_count; i++) + Stencil title = score_title (header); + if (title.is_empty ()) + title = score_title (header_); + if (!title.is_empty ()) { - if (Stencil *title = this->title (i)) - lines = ly_snoc (stencil2line (title, true), lines); - lines = scm_append (scm_list_2 (lines, scm_vector_to_list (scores_[i]))); + /* + TODO: this should come from the \layout {} block, which should + override settings from \paper {} + */ + SCM props + = paper_->lookup_variable (ly_symbol2scm ("score-title-properties")); + Prob *ps = make_paper_system (props); + paper_system_set_stencil (ps, title); + + return ps->self_scm (); } - //debug helper; ughr - int i = 0; - for (SCM s = lines; s != SCM_EOL; s = ly_cdr (s)) - unsmob_paper_line (ly_car (s))->number_ = ++i; - return lines; + + return SCM_BOOL_F; } -Link_array* -Paper_book::pages () +SCM +Paper_book::get_system_specs () { - init (); - Page::page_count_ = 0; - Paper_def *paper = papers_[0]; - Page *page = new Page (paper, 1); - - Real text_height = page->text_height (); - - Real copy_height = 0; - if (Stencil *s = unsmob_stencil (copyright_)) - copy_height = s->extent (Y_AXIS).length (); - - Real tag_height = 0; - if (Stencil *s = unsmob_stencil (tagline_)) - tag_height = s->extent (Y_AXIS).length (); - - SCM all = lines (); - SCM proc = paper->get_scmvar ("page-breaking"); - SCM breaks = scm_apply_0 (proc, scm_list_n (all, scm_make_real (height_), - scm_make_real (text_height), - scm_make_real (-copy_height), - scm_make_real (-tag_height), - SCM_UNDEFINED)); - - /* Copyright on first page. */ - if (unsmob_stencil (copyright_)) - page->copyright_ = copyright_; - - Link_array *pages = new Link_array; - int page_count = SCM_VECTOR_LENGTH ((SCM) breaks); - int line = 1; - for (int i = 0; i < page_count; i++) + SCM system_specs = SCM_EOL; + + Stencil title = book_title (); + if (!title.is_empty ()) { - if (i) - page = new Page (paper, i+1); - int next = i + 1 < page_count - ? ly_scm2int (scm_vector_ref (breaks, scm_int2num (i))) : 0; - while ((!next && all != SCM_EOL) || line <= next) - { - SCM s = ly_car (all); - page->lines_ = ly_snoc (s, page->lines_); - page->height_ += unsmob_paper_line (s)->dim ()[Y_AXIS]; - page->line_count_++; - all = ly_cdr (all); - line++; - } - pages->push (page); + 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 (); } - /* Tagline on last page. */ - if (unsmob_stencil (tagline_)) - page->tagline_ = tagline_; + SCM page_properties + = Lily::layout_extract_page_properties (paper_->self_scm ()); + + 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))) + { + header = scm_car (s); + if (scm_is_null (header_0_)) + header_0_ = header; + } + else if (Page_marker *page_marker = unsmob (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 (scm_car (s))) + { + if (Paper_score *pscore = dynamic_cast (mop)) + { + SCM title = get_score_title (header); + + if (scm_is_pair (system_specs)) + set_system_penalty (scm_car (system_specs), header); + + if (unsmob (title)) + { + system_specs = scm_cons (title, system_specs); + unsmob (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 = Lily::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 (t)); + + SCM footnotes = get_footnotes (unsmob (t)->expr ()); + ps->set_property ("footnotes", footnotes); + ps->set_property ("is-title", SCM_BOOL_T); + if (scm_is_eq (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 + assert (0); + } - return pages; + system_specs = scm_reverse_x (system_specs, SCM_EOL); + return system_specs; } -static SCM -c_ragged_page_breaks (SCM lines, Real book_height, Real text_height, - Real first, Real last) +SCM +Paper_book::systems () { - int page_number = 0; - int page_count = int (book_height / text_height + 0.5); - SCM breaks = SCM_EOL; - Real page_height = text_height + first; - Real h = 0; - int number = 0; - for (SCM s = lines; s != SCM_EOL; s = ly_cdr (s)) + if (scm_is_true (systems_)) + return systems_; + + systems_ = SCM_EOL; + if (scm_is_pair (bookparts_)) { - Paper_line *pl = unsmob_paper_line (ly_car (s)); - if (!pl->is_title () && h < page_height) - number++; - h += pl->dim ()[Y_AXIS]; - if (!pl->is_title () && h > page_height) - { - breaks = ly_snoc (scm_int2num (number), breaks); - page_number++; - page_height = text_height + (page_number == page_count) * last; - h = 0; - } - if (ly_cdr (s) == SCM_EOL) - breaks = ly_snoc (scm_int2num (pl->number_), breaks); + SCM system_list = SCM_EOL; + for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p)) + if (Paper_book *pbookpart = unsmob (scm_car (p))) + system_list = scm_cons (pbookpart->systems (), system_list); + systems_ = scm_append (scm_reverse_x (system_list, SCM_EOL)); + } + else + { + SCM specs = get_system_specs (); + for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s)) + { + if (Paper_score * pscore + = unsmob (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 (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 (scm_cadr (s)); + if (scm_is_null (perm)) + next->set_property ("penalty", scm_from_int (10001)); + else if (scm_is_eq (perm, ly_symbol2scm ("force"))) + next->set_property ("penalty", scm_from_int (-10001)); + } + } } - return scm_vector (breaks); + return systems_; } -LY_DEFINE (ly_ragged_page_breaks, "ly:ragged-page-breaks", - 5, 0, 0, (SCM lines, SCM book, SCM text, SCM first, SCM last), - "Return a vector with line numbers of page breaks.") +SCM +Paper_book::pages () +{ + if (scm_is_true (pages_)) + return pages_; + + pages_ = SCM_EOL; + if (scm_is_pair (bookparts_)) + { + for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p)) + if (Paper_book *pbookpart = unsmob (scm_car (p))) + pages_ = scm_cons (pbookpart->pages (), pages_); + pages_ = scm_append (scm_reverse_x (pages_, SCM_EOL)); + } + else if (scm_is_pair (scores_)) + { + SCM page_breaking = paper_->c_variable ("page-breaking"); + pages_ = scm_call_1 (page_breaking, 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_call_1 (page_stencil, scm_car (pages)); + + // Perform any user-supplied post-processing. + SCM post_process = paper_->c_variable ("page-post-process"); + if (ly_is_procedure (post_process)) + scm_call_2 (post_process, paper_->self_scm (), pages_); + + /* set systems_ from the pages */ + if (scm_is_false (systems_)) + { + systems_ = SCM_EOL; + for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p)) + { + Prob *page = unsmob (scm_car (p)); + SCM systems = page->get_property ("lines"); + systems_ = scm_cons (systems, systems_); + } + systems_ = scm_append (scm_reverse_x (systems_, SCM_EOL)); + } + } + return pages_; +} + +SCM +Paper_book::performances () const { - SCM_ASSERT_TYPE (scm_pair_p (lines), lines, SCM_ARG1, __FUNCTION__, "list"); - SCM_ASSERT_TYPE (is_number (book), book, SCM_ARG2, __FUNCTION__, "real"); - SCM_ASSERT_TYPE (is_number (text), text, SCM_ARG2, __FUNCTION__, "real"); - SCM_ASSERT_TYPE (is_number (first), first, SCM_ARG2, __FUNCTION__, "real"); - SCM_ASSERT_TYPE (is_number (last), last, SCM_ARG2, __FUNCTION__, "real"); - - return c_ragged_page_breaks (lines, - ly_scm2double (book), ly_scm2double (text), - ly_scm2double (first), ly_scm2double (last)); + return scm_reverse (performances_); }