Fix type predicates/docstrings for two music properties.
[lilypond/mpolesky.git] / lily / page-breaking.cc
blob7dbac44278ef34c2f9cd0073c599932306c5212f
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2006--2010 Joe Neeman <joeneeman@gmail.com>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
21 This is a utility class for page-breaking algorithms. There are some complex
22 parts of this class, some of which are useful to understand if you intend
23 to write a page breaking algorithm (ie. a subclass of Page_breaking). Most
24 of these complexities were introduced in order to break the problem of
25 page-breaking into simpler subproblems and to hide some of the bookkeeping
26 complexities of page breaking from the page breaking algorithms.
28 COMPRESSED LINES
29 There are several functions that actually distribute systems across pages
30 (for example, the space_systems_XXX and pack_systems_XXX functions). If
31 each of these functions had to handle \noPageBreak, it would be a mess.
32 Therefore, we handle \noPageBreak by "compressing" the list of systems
33 before doing any layout: we concatenate any two systems separated by a
34 \noPageBreak into a single system. The page-breaking functions can do their
35 magic without encountering a \noPageBreak; we then "uncompress" the systems
36 at the end. We almost always work with cached_line_details_, which are
37 "compressed."
39 CHUNKS
40 The basic operation of a page breaking algorithm is to repeatedly request
41 some systems from the line-breaker and place those systems on some pages.
42 With each repetition, the page breaking algorithm asks the line-breaker for
43 some systems that it thinks will help it achieve a better layout. The
44 Page_breaking class provides functionality to facilitate this in the case
45 that the page breaking algorithm only cares about the number of systems.
47 Even if a page breaking algorithm only cares number of systems, there may
48 be many ways to satisfy its request. For example, in a piece with 2 scores
49 and a request for 10 systems, we could return 5 systems from each score or
50 4 from the first and 6 from the second. Even within a score, we might
51 want to try several different line breaking configurations with a fixed
52 system count; if there is a forced \pageBreak, for example, we might wish
53 to tweak the number of systems on both sides of the \pageBreak independently.
55 The Page_breaking class takes care of finding these configurations. It
56 divides the piece into "chunks" and sets up the line-breaker in such a way
57 that the number of systems in each chunk can be modified independently.
58 Chunks never cross score boundaries; each title and markup is its own chunk.
59 When a page breaking algorithm requests a number of systems, the Page_breaker
60 stores an array of potential configurations, which the page breaking
61 algorithm can iterate over using current_configuration(vsize).
63 LINE_DIVISION
64 A Line_division is simply a way of storing the exact way in which the
65 total number of systems is distributed among chunks. Note that a
66 Line_division may not (in fact, usually will not) describe all of the chunks
67 in the entire book. Rather, it will describe the subset of chunks that lie
68 between some fixed starting and ending point. This subset of chunks changes
69 whenever a page breaking algorithm asks to consider a different pair of
70 starting and ending breakpoints. In particular, a Line_division should be
71 discarded after a call to set_current_breakpoints, since that Line_division
72 refers to a subset of chunks which might be different from the current
73 subset of chunks under consideration.
76 #include "page-breaking.hh"
78 #include "international.hh"
79 #include "item.hh"
80 #include "output-def.hh"
81 #include "page-layout-problem.hh"
82 #include "page-spacing.hh"
83 #include "paper-book.hh"
84 #include "paper-score.hh"
85 #include "paper-system.hh"
86 #include "system.hh"
87 #include "warn.hh"
89 /* for each forbidden page break, merge the systems around it into one
90 system. */
91 static vector<Line_details>
92 compress_lines (const vector<Line_details> &orig)
94 vector<Line_details> ret;
96 for (vsize i = 0; i < orig.size (); i++)
98 if (ret.size () && !scm_is_symbol (ret.back ().page_permission_))
100 Line_details const &old = ret.back ();
101 Line_details compressed = orig[i];
102 Real padding = orig[i].title_ ? old.title_padding_ : old.padding_;
104 compressed.extent_[DOWN] = old.extent_[DOWN];
105 compressed.extent_[UP] = old.extent_[UP] + orig[i].extent_.length () + padding;
106 compressed.space_ += old.space_;
107 compressed.inverse_hooke_ += old.inverse_hooke_;
109 compressed.compressed_lines_count_ = old.compressed_lines_count_ + 1;
110 compressed.compressed_nontitle_lines_count_ =
111 old.compressed_nontitle_lines_count_ + (compressed.title_ ? 0 : 1);
113 // compressed.title_ is true if and only if the first of its
114 // compressed lines was a title.
115 compressed.title_ = old.title_;
116 ret.back () = compressed;
118 else
120 ret.push_back (orig[i]);
121 ret.back ().force_ = 0;
124 return ret;
127 /* translate the number of systems-per-page into something meaningful for
128 the uncompressed lines.
130 static vector<vsize>
131 uncompress_solution (vector<vsize> const &systems_per_page,
132 vector<Line_details> const &compressed)
134 vector<vsize> ret;
135 vsize start_sys = 0;
137 for (vsize i = 0; i < systems_per_page.size (); i++)
139 int compressed_count = 0;
140 for (vsize j = start_sys; j < start_sys + systems_per_page[i]; j++)
141 compressed_count += compressed[j].compressed_lines_count_ - 1;
143 ret.push_back (systems_per_page[i] + compressed_count);
144 start_sys += systems_per_page[i];
146 return ret;
149 /* for Page_breaking, the start index (when we are dealing with the stuff
150 between a pair of breakpoints) refers to the break_ index of the end of
151 the previous page. So the system index of the start of the current page
152 could either be the same as the end of the previous page or one more than
153 it. */
155 /* Turn a break index into the sys index that starts the next page */
156 vsize
157 Page_breaking::next_system (Break_position const &break_pos) const
159 vsize sys = break_pos.system_spec_index_;
161 if (sys == VPOS) /* beginning of the book */
162 return 0;
163 if (system_specs_[sys].pscore_ && !break_pos.score_ender_)
164 return sys; /* the score overflows the previous page */
165 return sys + 1; /* this page starts with a new System_spec */
168 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break)
170 book_ = pb;
171 system_count_ = 0;
172 ragged_ = to_boolean (pb->paper_->c_variable ("ragged-bottom"));
173 ragged_last_ = to_boolean (pb->paper_->c_variable ("ragged-last-bottom"));
174 systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("systems-per-page"), 0));
175 max_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("max-systems-per-page"), 0));
176 min_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("min-systems-per-page"), 0));
177 orphan_penalty_ = robust_scm2int (pb->paper_->c_variable ("orphan-penalty"), 100000);
179 if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_))
181 warning (_f ("ignoring min-systems-per-page and max-systems-per-page because systems-per-page was set"));
182 min_systems_per_page_ = max_systems_per_page_ = 0;
184 if (max_systems_per_page_ && min_systems_per_page_ > max_systems_per_page_)
186 warning (_f ("min-systems-per-page is larger than max-systems-per-page, ignoring both values"));
187 min_systems_per_page_ = max_systems_per_page_ = 0;
190 create_system_list ();
191 find_chunks_and_breaks (is_break);
194 Page_breaking::~Page_breaking ()
198 bool
199 Page_breaking::ragged () const
201 return ragged_;
204 bool
205 Page_breaking::ragged_last () const
207 return ragged_last_;
211 Page_breaking::systems_per_page () const
213 return systems_per_page_;
217 Page_breaking::max_systems_per_page () const
219 return max_systems_per_page_;
223 Page_breaking::min_systems_per_page () const
225 return min_systems_per_page_;
228 vsize
229 Page_breaking::system_count () const
231 return system_count_;
234 bool
235 Page_breaking::too_many_lines (int line_count) const
237 return max_systems_per_page () > 0 && line_count > max_systems_per_page ();
240 bool
241 Page_breaking::too_few_lines (int line_count) const
243 return line_count < min_systems_per_page ();
246 Real
247 Page_breaking::line_count_penalty (int line_count) const
249 if (too_many_lines (line_count))
250 return (line_count - max_systems_per_page ()) * TERRIBLE_SPACING_PENALTY;
251 if (too_few_lines (line_count))
252 return (min_systems_per_page () - line_count) * TERRIBLE_SPACING_PENALTY;
254 return 0;
258 Page_breaking::line_count_status (int line_count) const
260 if (too_many_lines (line_count))
261 return SYSTEM_COUNT_TOO_MANY;
262 if (too_few_lines (line_count))
263 return SYSTEM_COUNT_TOO_FEW;
265 return SYSTEM_COUNT_OK;
268 /* translate indices into breaks_ into start-end parameters for the line breaker */
269 void
270 Page_breaking::line_breaker_args (vsize sys,
271 Break_position const &start,
272 Break_position const &end,
273 vsize *line_breaker_start,
274 vsize *line_breaker_end)
276 assert (system_specs_[sys].pscore_);
277 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
279 if (start.system_spec_index_ == sys)
280 *line_breaker_start = start.score_break_;
281 else
282 *line_breaker_start = 0;
284 if (end.system_spec_index_ == sys)
285 *line_breaker_end = end.score_break_;
286 else
287 *line_breaker_end = VPOS;
290 void
291 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
292 Line_division const &div)
294 vector<Break_position> chunks = chunk_list (start_break, end_break);
295 bool ignore_div = false;
296 if (chunks.size () != div.size () + 1)
298 programming_error ("did not find a valid page breaking configuration");
299 ignore_div = true;
302 for (vsize i = 0; i + 1 < chunks.size (); i++)
304 vsize sys = next_system (chunks[i]);
305 if (system_specs_[sys].pscore_)
307 vsize start;
308 vsize end;
309 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
311 vector<Column_x_positions> pos = ignore_div
312 ? line_breaking_[sys].best_solution (start, end)
313 : line_breaking_[sys].solve (start, end, div[i]);
314 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
320 Page_breaking::systems ()
322 SCM ret = SCM_EOL;
323 for (vsize sys = 0; sys < system_specs_.size (); sys++)
325 if (system_specs_[sys].pscore_)
327 system_specs_[sys].pscore_->root_system ()
328 ->do_break_substitution_and_fixup_refpoints ();
329 SCM lines = system_specs_[sys].pscore_->root_system ()
330 ->get_broken_system_grobs ();
331 ret = scm_cons (lines, ret);
333 else
335 Prob *pb = system_specs_[sys].prob_;
336 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
337 pb->unprotect ();
340 return scm_append (scm_reverse (ret));
344 Page_breaking::make_page (int page_num, bool last) const
346 bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
347 SCM mod = scm_c_resolve_module ("scm page");
348 SCM make_page_scm = scm_c_module_lookup (mod, "make-page");
350 make_page_scm = scm_variable_ref (make_page_scm);
352 return scm_apply_0 (make_page_scm,
353 scm_list_n (book_->self_scm (),
354 ly_symbol2scm ("page-number"), scm_from_int (page_num),
355 ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
356 ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
357 SCM_UNDEFINED));
360 Real
361 Page_breaking::page_height (int page_num, bool last) const
363 SCM mod = scm_c_resolve_module ("scm page");
364 SCM page = make_page (page_num, last);
365 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
366 calc_height = scm_variable_ref (calc_height);
368 SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
369 return scm_to_double (height);
373 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
375 Break_position const &pos = breaks_[breakpoint];
377 if (pos.system_spec_index_ == VPOS)
378 return SCM_EOL;
379 if (system_specs_[pos.system_spec_index_].pscore_)
380 return pos.col_->get_property (str);
381 return system_specs_[pos.system_spec_index_].prob_->get_property (str);
385 Page_breaking::get_page_configuration (SCM systems, int page_num, bool ragged, bool last)
387 SCM dummy_page = make_page (page_num, last);
388 Page_layout_problem layout (book_, dummy_page, systems);
389 return scm_is_pair (systems) ? layout.solution (ragged) : SCM_EOL;
393 Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool last)
395 // Create a stencil for each system.
396 SCM paper_systems = SCM_EOL;
397 for (SCM s = scm_reverse (systems); scm_is_pair (s); s = scm_cdr (s))
399 SCM paper_system = scm_car (s);
400 if (Grob *g = unsmob_grob (scm_car (s)))
402 System *sys = dynamic_cast<System*> (g);
403 paper_system = sys->get_paper_system ();
406 paper_systems = scm_cons (paper_system, paper_systems);
409 // Create the page and draw it.
410 SCM page = make_page (page_num, last);
411 SCM page_module = scm_c_resolve_module ("scm page");
412 SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
413 page_stencil = scm_variable_ref (page_stencil);
415 Prob *p = unsmob_prob (page);
416 p->set_property ("lines", paper_systems);
417 p->set_property ("configuration", configuration);
418 scm_apply_1 (page_stencil, page, SCM_EOL);
420 return page;
424 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
426 int first_page_number
427 = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
428 SCM ret = SCM_EOL;
429 SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
430 if (label_page_table == SCM_UNDEFINED)
431 label_page_table = SCM_EOL;
433 // Build a list of (systems . configuration) pairs. Note that we lay out
434 // the staves and find the configurations before drawing anything. Some
435 // grobs (like tuplet brackets) look at their neighbours while drawing
436 // themselves. If this happens before the neighbouring staves have
437 // been laid out, bad side-effects could happen (in particular,
438 // Align_interface::align_to_ideal_distances might be called).
439 SCM systems_and_configs = SCM_EOL;
441 for (vsize i = 0; i < lines_per_page.size (); i++)
443 int page_num = i + first_page_number;
444 bool bookpart_last_page = (i == lines_per_page.size () - 1);
445 bool rag = ragged () || (bookpart_last_page && ragged_last ());
446 SCM line_count = scm_from_int (lines_per_page[i]);
447 SCM lines = scm_list_head (systems, line_count);
448 SCM config = get_page_configuration (lines, page_num, rag, bookpart_last_page);
450 systems_and_configs = scm_cons (scm_cons (lines, config), systems_and_configs);
451 systems = scm_list_tail (systems, line_count);
454 // Now it's safe to make the pages.
455 int page_num = first_page_number + lines_per_page.size () - 1;
456 for (SCM s = systems_and_configs; scm_is_pair (s); s = scm_cdr (s))
458 SCM lines = scm_caar (s);
459 SCM config = scm_cdar (s);
460 bool bookpart_last_page = (s == systems_and_configs);
461 SCM page = draw_page (lines, config, page_num, bookpart_last_page);
463 /* collect labels */
464 SCM page_num_scm = scm_from_int (page_num);
465 for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
467 SCM labels = SCM_EOL;
468 if (Grob * line = unsmob_grob (scm_car (l)))
470 System *system = dynamic_cast<System*> (line);
471 labels = system->get_property ("labels");
473 else if (Prob *prob = unsmob_prob (scm_car (l)))
474 labels = prob->get_property ("labels");
476 for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
477 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num_scm),
478 label_page_table);
481 ret = scm_cons (page, ret);
482 --page_num;
484 book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
485 return ret;
488 /* The page-turn-page-breaker needs to have a line-breaker between any two
489 columns with non-NULL page-turn-permission.
491 The optimal-breaker needs to have a line-breaker between any two columns
492 with page-break-permission = 'force.
494 By using a grob predicate, we can accommodate both of these uses.
496 void
497 Page_breaking::create_system_list ()
499 SCM specs = book_->get_system_specs ();
500 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
502 if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
504 system_specs_.push_back (System_spec (ps));
506 else
508 Prob *pb = unsmob_prob (scm_car (s));
509 assert (pb);
511 pb->protect ();
512 system_specs_.push_back (System_spec (pb));
517 void
518 Page_breaking::find_chunks_and_breaks (Break_predicate is_break)
520 SCM force_sym = ly_symbol2scm ("force");
522 chunks_.push_back (Break_position ());
523 breaks_.push_back (Break_position ());
525 for (vsize i = 0; i < system_specs_.size (); i++)
527 if (system_specs_[i].pscore_)
529 vector<Grob*> cols;
531 SCM system_count = system_specs_[i].pscore_->layout ()->c_variable ("system-count");
532 if (scm_is_number (system_count))
534 // With system-count given, the line configuration for
535 // this score is fixed. We need to ensure that chunk
536 // boundaries only occur at line breaks.
537 Constrained_breaking breaking (system_specs_[i].pscore_);
538 vector<Line_details> details = breaking.line_details (0, VPOS, scm_to_int (system_count));
540 cols.push_back (system_specs_[i].pscore_->root_system ()->used_columns ()[0]);
541 for (vsize j = 0; j < details.size (); j++)
542 cols.push_back (details[j].last_column_);
544 else
545 cols = system_specs_[i].pscore_->root_system ()->used_columns ();
547 int last_chunk_idx = 0;
548 vector<vsize> line_breaker_columns;
549 line_breaker_columns.push_back (0);
551 for (vsize j = 1; j < cols.size (); j++)
553 bool last = (j == cols.size () - 1);
554 bool break_point = is_break (cols[j]);
555 bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
556 Break_position cur_pos = Break_position (i,
557 line_breaker_columns.size (),
558 cols[j],
559 last);
561 // NOTE: even in the breaks_ list, forced_line_count_
562 // refers to the number of lines between a
563 // Break_position and the start of that /chunk/. This
564 // is needed for system_count_bounds to work correctly,
565 // since it mixes Break_positions from breaks_ and
566 // chunks_.
567 if (scm_is_number (system_count))
568 cur_pos.forced_line_count_ = j - last_chunk_idx;
570 if (break_point || (i == system_specs_.size () - 1 && last))
571 breaks_.push_back (cur_pos);
572 if (chunk_end || last)
574 chunks_.push_back (cur_pos);
575 last_chunk_idx = j;
578 if ((break_point || chunk_end) && !last)
579 line_breaker_columns.push_back (j);
581 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
583 else
585 /* TODO: we want some way of applying Break_p to a prob? */
586 if (i == system_specs_.size () - 1)
587 breaks_.push_back (Break_position (i));
589 chunks_.push_back (Break_position (i));
591 /* FIXME: shouldn't we push a Null_breaker or similar dummy
592 class? --hwn */
593 line_breaking_.push_back (Constrained_breaking (NULL));
598 vector<Break_position>
599 Page_breaking::chunk_list (vsize start_index, vsize end_index)
601 Break_position start = breaks_[start_index];
602 Break_position end = breaks_[end_index];
604 vsize i = 0;
605 for (; i < chunks_.size () && chunks_[i] <= start; i++)
608 vector<Break_position> ret;
609 ret.push_back (start);
610 for (; i < chunks_.size () && chunks_[i] < end; i++)
611 ret.push_back (chunks_[i]);
612 ret.push_back (end);
613 return ret;
616 vsize
617 Page_breaking::min_system_count (vsize start, vsize end)
619 vector<Break_position> chunks = chunk_list (start, end);
620 Line_division div = system_count_bounds (chunks, true);
621 vsize ret = 0;
623 for (vsize i = 0; i < div.size (); i++)
624 ret += div[i];
625 return ret;
628 vsize
629 Page_breaking::max_system_count (vsize start, vsize end)
631 vector<Break_position> chunks = chunk_list (start, end);
632 Line_division div = system_count_bounds (chunks, false);
633 vsize ret = 0;
635 for (vsize i = 0; i < div.size (); i++)
636 ret += div[i];
637 return ret;
640 Page_breaking::Line_division
641 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
642 bool min)
644 assert (chunks.size () >= 2);
646 Line_division ret;
647 ret.resize (chunks.size () - 1, 1);
649 for (vsize i = 0; i + 1 < chunks.size (); i++)
651 vsize sys = next_system (chunks[i]);
653 if (chunks[i+1].forced_line_count_)
654 ret[i] = chunks[i+1].forced_line_count_;
655 else if (system_specs_[sys].pscore_)
657 vsize start;
658 vsize end;
659 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
660 ret[i] = min
661 ? line_breaking_[sys].min_system_count (start, end)
662 : line_breaking_[sys].max_system_count (start, end);
666 return ret;
669 void
670 Page_breaking::set_current_breakpoints (vsize start,
671 vsize end,
672 vsize system_count,
673 Line_division lower_bound,
674 Line_division upper_bound)
676 system_count_ = system_count;
677 current_chunks_ = chunk_list (start, end);
678 current_start_breakpoint_ = start;
679 current_end_breakpoint_ = end;
680 clear_line_details_cache ();
682 if (!lower_bound.size ())
683 lower_bound = system_count_bounds (current_chunks_, true);
684 if (!upper_bound.size ())
685 upper_bound = system_count_bounds (current_chunks_, false);
687 assert (lower_bound.size () == current_chunks_.size () - 1);
688 assert (upper_bound.size () == current_chunks_.size () - 1);
690 Line_division work_in_progress;
691 current_configurations_.clear ();
692 line_divisions_rec (system_count,
693 lower_bound,
694 upper_bound,
695 &work_in_progress);
697 /* we only consider a constant number of configurations. Otherwise,
698 this becomes slow when there are many small scores. The constant
699 5 is somewhat arbitrary. */
700 if (current_configurations_.size () > 5)
702 vector<pair<Real,vsize> > dems_and_indices;
704 for (vsize i = 0; i < current_configurations_.size (); i++)
706 cache_line_details (i);
707 Real dem = 0;
708 for (vsize j = 0; j < cached_line_details_.size (); j++)
709 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
710 + cached_line_details_[j].break_penalty_;
712 dems_and_indices.push_back (pair<Real,vsize> (dem, i));
714 vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
716 vector<Line_division> best_5_configurations;
717 for (vsize i = 0; i < 5; i++)
718 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
720 clear_line_details_cache ();
721 current_configurations_ = best_5_configurations;
725 void
726 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
728 current_chunks_ = chunk_list (start, end);
729 current_start_breakpoint_ = start;
730 current_end_breakpoint_ = end;
731 clear_line_details_cache ();
732 system_count_ = 0;
734 Line_division div;
735 for (vsize i = 0; i+1 < current_chunks_.size (); i++)
737 vsize sys = next_system (current_chunks_[i]);
739 if (current_chunks_[i+1].forced_line_count_)
740 div.push_back (current_chunks_[i+1].forced_line_count_);
741 else if (system_specs_[sys].pscore_)
743 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
744 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
746 else
747 div.push_back (1);
749 system_count_ += div.back ();
751 current_configurations_.clear ();
752 current_configurations_.push_back (div);
755 vsize
756 Page_breaking::current_configuration_count () const
758 return current_configurations_.size ();
761 void
762 Page_breaking::cache_line_details (vsize configuration_index)
764 if (cached_configuration_index_ != configuration_index)
766 cached_configuration_index_ = configuration_index;
768 Line_division &div = current_configurations_[configuration_index];
769 uncompressed_line_details_.clear ();
770 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
772 vsize sys = next_system (current_chunks_[i]);
773 if (system_specs_[sys].pscore_)
775 vsize start;
776 vsize end;
777 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
779 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
780 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
782 else
784 assert (div[i] == 1);
785 uncompressed_line_details_.push_back (Line_details (system_specs_[sys].prob_, book_->paper_));
788 cached_line_details_ = compress_lines (uncompressed_line_details_);
792 void
793 Page_breaking::clear_line_details_cache ()
795 cached_configuration_index_ = VPOS;
796 cached_line_details_.clear ();
797 uncompressed_line_details_.clear ();
800 void
801 Page_breaking::line_divisions_rec (vsize system_count,
802 Line_division const &min_sys,
803 Line_division const &max_sys,
804 Line_division *cur_division)
806 vsize my_index = cur_division->size ();
807 vsize others_min = 0;
808 vsize others_max = 0;
810 for (vsize i = my_index + 1; i < min_sys.size (); i++)
812 others_min += min_sys[i];
813 others_max += max_sys[i];
815 others_max = min (others_max, system_count);
816 vsize real_min = max (min_sys[my_index], system_count - others_max);
817 vsize real_max = min (max_sys[my_index], system_count - others_min);
819 if (real_min > real_max || real_min <= 0)
821 /* this should never happen within a recursive call. If it happens
822 at all, it means that we were called with an unsolvable problem
823 and we should return an empty result */
824 assert (my_index == 0);
825 return;
828 for (vsize i = real_min; i <= real_max; i++)
830 cur_division->push_back (i);
831 if (my_index == min_sys.size () - 1)
832 current_configurations_.push_back (*cur_division);
833 else
834 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
835 cur_division->pop_back ();
839 vsize
840 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
842 vsize ret = 1;
843 vsize page_starter = 0;
844 Real cur_rod_height = 0;
845 Real cur_spring_height = 0;
846 Real cur_page_height = page_height (first_page_num, false);
847 int line_count = 0;
849 cache_line_details (configuration);
851 if (cached_line_details_.size ())
852 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[0]);
854 for (vsize i = 0; i < cached_line_details_.size (); i++)
856 Real ext_len = cached_line_details_[i].extent_.length ();
857 Real padding = 0;
858 if (cur_rod_height > 0)
859 padding = cached_line_details_[i].title_ ?
860 cached_line_details_[i-1].title_padding_ : cached_line_details_[i-1].padding_;
862 Real next_rod_height = cur_rod_height + ext_len + padding;
863 Real next_spring_height = cur_spring_height + cached_line_details_[i].space_;
864 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0)
865 + min_whitespace_at_bottom_of_page (cached_line_details_[i]);
866 int next_line_count = line_count + cached_line_details_[i].compressed_nontitle_lines_count_;
868 if ((!too_few_lines (line_count) && (next_height > cur_page_height && cur_rod_height > 0))
869 || too_many_lines (next_line_count)
870 || (i > 0
871 && cached_line_details_[i-1].page_permission_ == ly_symbol2scm ("force")))
873 line_count = cached_line_details_[i].compressed_nontitle_lines_count_;
874 cur_rod_height = ext_len;
875 cur_spring_height = cached_line_details_[i].space_;
876 page_starter = i;
878 cur_page_height = page_height (first_page_num + ret, false);
879 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[i]);
881 ret++;
883 else
885 cur_rod_height = next_rod_height;
886 cur_spring_height = next_spring_height;
887 line_count = next_line_count;
891 /* there are two potential problems with the last page (because we didn't know
892 it was the last page until after we managed to fit all the systems to it):
893 - we are ragged-last but the last page has a compressed spring
894 - the value returned by page_height (num, true) is smaller than the
895 value returned by page_height (num, false) and it causes the page not to
896 fit.
898 In either case, we just need to add one more page. This is because the last
899 line will always fit on the extra page and by adding one more page to the
900 end, the previous page is no longer the last page, so our previous
901 calculations that treated it as a non-last page were ok.
904 cur_page_height = page_height (first_page_num + ret - 1, true);
905 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[page_starter]);
906 cur_page_height -= min_whitespace_at_bottom_of_page (cached_line_details_.back ());
908 Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
909 if (!too_few_lines (line_count - cached_line_details_.back ().compressed_nontitle_lines_count_)
910 && cur_height > cur_page_height
911 /* don't increase the page count if the last page had only one system */
912 && cur_rod_height > cached_line_details_.back ().extent_.length ())
913 ret++;
915 assert (ret <= cached_line_details_.size ());
916 return ret;
919 // If systems_per_page_ is positive, we don't really try to space on N pages;
920 // we just put the requested number of systems on each page and penalize
921 // if the result doesn't have N pages.
922 Page_spacing_result
923 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
925 Page_spacing_result ret;
927 if (systems_per_page_ > 0)
929 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
930 ret.demerits_ += (ret.force_.size () == n) ? 0 : BAD_SPACING_PENALTY;
931 return ret;
934 cache_line_details (configuration);
935 bool valid_n = (n >= min_page_count (configuration, first_page_num)
936 && n <= cached_line_details_.size ());
938 if (!valid_n)
939 programming_error ("number of pages is out of bounds");
941 if (n == 1 && valid_n)
942 ret = space_systems_on_1_page (cached_line_details_,
943 page_height (first_page_num, is_last ()),
944 ragged () || (is_last () && ragged_last ()));
945 else if (n == 2 && valid_n)
946 ret = space_systems_on_2_pages (configuration, first_page_num);
947 else
949 Page_spacer ps (cached_line_details_, first_page_num, this);
950 ret = ps.solve (n);
953 return finalize_spacing_result (configuration, ret);
956 Real
957 Page_breaking::blank_page_penalty () const
959 SCM penalty_sym;
961 if (is_last ())
962 penalty_sym = ly_symbol2scm ("blank-last-page-force");
963 else if (ends_score ())
964 penalty_sym = ly_symbol2scm ("blank-after-score-page-force");
965 else
966 penalty_sym = ly_symbol2scm ("blank-page-force");
968 Break_position const &pos = breaks_[current_end_breakpoint_];
969 if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
970 return robust_scm2double (ps->layout ()->lookup_variable (penalty_sym), 0.0);
972 return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
975 // If systems_per_page_ is positive, we don't really try to space on N
976 // or N+1 pages; see the comment to space_systems_on_n_pages.
977 Page_spacing_result
978 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num,
979 Real penalty_for_fewer_pages)
981 Page_spacing_result n_res;
982 Page_spacing_result m_res;
984 if (systems_per_page_ > 0)
986 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
987 ret.demerits_ += (ret.force_.size () == n || ret.force_.size () == (n-1)) ? 0 : BAD_SPACING_PENALTY;
988 return ret;
991 cache_line_details (configuration);
992 vsize min_p_count = min_page_count (configuration, first_page_num);
993 bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
995 if (!valid_n)
996 programming_error ("both page counts are out of bounds");
998 if (n == 1 && valid_n)
1000 bool rag = ragged () || (is_last () && ragged_last ());
1001 Real height = page_height (first_page_num, is_last ());
1003 if (1 >= min_p_count)
1004 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
1005 if (1 < cached_line_details_.size ())
1006 m_res = space_systems_on_2_pages (configuration, first_page_num);
1008 else
1010 Page_spacer ps (cached_line_details_, first_page_num, this);
1012 if (n >= min_p_count || !valid_n)
1013 n_res = ps.solve (n);
1014 if (n < cached_line_details_.size () || !valid_n)
1015 m_res = ps.solve (n+1);
1018 m_res = finalize_spacing_result (configuration, m_res);
1019 n_res = finalize_spacing_result (configuration, n_res);
1021 Real page_spacing_weight = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1022 n_res.demerits_ += penalty_for_fewer_pages * page_spacing_weight;
1024 if (n_res.force_.size ())
1025 n_res.force_.back () += penalty_for_fewer_pages;
1027 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
1030 Page_spacing_result
1031 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
1033 vsize min_p_count = min_page_count (configuration, first_page_num);
1035 cache_line_details (configuration);
1036 Page_spacer ps (cached_line_details_, first_page_num, this);
1037 Page_spacing_result best = ps.solve (min_p_count);
1039 for (vsize i = min_p_count+1; i <= cached_line_details_.size (); i++)
1041 Page_spacing_result cur = ps.solve (i);
1042 if (cur.demerits_ < best.demerits_)
1043 best = cur;
1046 Page_spacing_result ret = finalize_spacing_result (configuration, best);
1047 return ret;
1050 Page_spacing_result
1051 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
1052 vsize first_page_num)
1054 Page_spacing_result res;
1055 Page_spacing space (page_height (first_page_num, false), this);
1056 vsize line = 0;
1057 vsize page = 0;
1058 vsize page_first_line = 0;
1060 cache_line_details (configuration);
1061 while (line < cached_line_details_.size ())
1063 page++;
1064 space.clear ();
1065 space.resize (page_height (first_page_num + page, false));
1067 int system_count_on_this_page = 0;
1068 while (system_count_on_this_page < systems_per_page_
1069 && line < cached_line_details_.size ())
1071 Line_details const &cur_line = cached_line_details_[line];
1072 space.append_system (cur_line);
1073 system_count_on_this_page += cur_line.compressed_nontitle_lines_count_;
1074 line++;
1076 if (cur_line.page_permission_ == ly_symbol2scm ("force"))
1077 break;
1080 res.systems_per_page_.push_back (line - page_first_line);
1082 res.force_.push_back (space.force_);
1083 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1084 if (system_count_on_this_page != systems_per_page_)
1086 res.penalty_ += TERRIBLE_SPACING_PENALTY;
1087 res.system_count_status_ |= ((system_count_on_this_page < systems_per_page_))
1088 ? SYSTEM_COUNT_TOO_FEW : SYSTEM_COUNT_TOO_MANY;
1091 page_first_line = line;
1094 /* Recalculate forces for the last page because we know now that is
1095 really the last page. */
1096 space.resize (page_height (first_page_num + page, true));
1097 res.force_.back () = space.force_;
1098 return finalize_spacing_result (configuration, res);
1101 Page_spacing_result
1102 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
1104 // TODO: add support for min/max-systems-per-page.
1105 Page_spacing_result res;
1106 vsize page = 0;
1107 vsize page_first_line = 0;
1108 Page_spacing space (page_height (first_page_num, false), this);
1110 cache_line_details (configuration);
1111 for (vsize line = 0; line < cached_line_details_.size (); line++)
1113 Real prev_force = space.force_;
1114 space.append_system (cached_line_details_[line]);
1115 if ((line > page_first_line)
1116 && (isinf (space.force_)
1117 || ((line > 0)
1118 && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
1120 res.systems_per_page_.push_back (line - page_first_line);
1121 res.force_.push_back (prev_force);
1122 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1123 page++;
1124 space.resize (page_height (first_page_num + page, false));
1125 space.clear ();
1126 space.append_system (cached_line_details_[line]);
1127 page_first_line = line;
1130 if (line == cached_line_details_.size () - 1)
1132 /* This is the last line */
1133 /* When the last page height was computed, we did not know yet that it
1134 * was the last one. If the systems put on it don't fit anymore, the last
1135 * system is moved to a new page */
1136 space.resize (page_height (first_page_num + page, true));
1137 if ((line > page_first_line) && (isinf (space.force_)))
1139 res.systems_per_page_.push_back (line - page_first_line);
1140 res.force_.push_back (prev_force);
1141 /* the last page containing the last line */
1142 space.resize (page_height (first_page_num + page + 1, true));
1143 space.clear ();
1144 space.append_system (cached_line_details_[line]);
1145 res.systems_per_page_.push_back (1);
1146 res.force_.push_back (space.force_);
1147 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1148 res.penalty_ += cached_line_details_[line].page_penalty_;
1150 else
1152 res.systems_per_page_.push_back (line + 1 - page_first_line);
1153 res.force_.push_back (space.force_);
1154 res.penalty_ += cached_line_details_[line].page_penalty_;
1158 return finalize_spacing_result (configuration, res);
1161 /* Calculate demerits and fix res.systems_per_page_ so that
1162 it refers to the original line numbers, not the ones given by compress_lines (). */
1163 Page_spacing_result
1164 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
1166 if (res.force_.empty ())
1167 return res;
1169 cache_line_details (configuration);
1170 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
1172 Real line_force = 0;
1173 Real line_penalty = 0;
1174 Real page_demerits = res.penalty_;
1175 Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1177 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
1179 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
1180 line_penalty += uncompressed_line_details_[i].break_penalty_;
1183 for (vsize i = 0; i < res.force_.size (); i++)
1185 Real f = res.force_[i];
1187 page_demerits += min(f*f, BAD_SPACING_PENALTY);
1190 /* for a while we tried averaging page and line forces across pages instead
1191 of summing them, but it caused a problem: if there is a single page
1192 with a very bad page force (for example because of a forced page break),
1193 the page breaker will put in a _lot_ of pages so that the bad force
1194 becomes averaged out over many pages. */
1195 res.demerits_ = line_force + line_penalty + page_demerits * page_weighting;
1196 return res;
1200 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
1201 are by far the most common cases, we have special functions for them.
1203 space_systems_on_1_page has a different calling convention than most of the
1204 space_systems functions. This is because space_systems_on_1_page is (unlike
1205 the other space_systems functions) sometimes called on subsets of a full
1206 configuration. */
1207 Page_spacing_result
1208 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
1210 Page_spacing space (page_height, this);
1211 Page_spacing_result ret;
1212 int line_count = 0;
1214 for (vsize i = 0; i < lines.size (); i++)
1216 space.append_system (lines[i]);
1217 line_count += lines[i].compressed_nontitle_lines_count_;
1220 ret.systems_per_page_.push_back (lines.size ());
1221 ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
1222 ret.penalty_ = line_count_penalty (line_count) + lines.back ().page_penalty_ + lines.back ().turn_penalty_;
1223 ret.system_count_status_ |= line_count_status (line_count);
1225 /* don't do finalize_spacing_result () because we are only an internal function */
1226 return ret;
1229 Page_spacing_result
1230 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
1232 Real page1_height = page_height (first_page_num, false);
1233 Real page2_height = page_height (first_page_num + 1, is_last ());
1234 bool ragged1 = ragged ();
1235 bool ragged2 = ragged () || (is_last () && ragged_last ());
1237 /* if there is a forced break, this reduces to 2 1-page problems */
1238 cache_line_details (configuration);
1239 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
1240 if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
1242 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
1243 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
1244 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
1245 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
1247 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
1248 p1.force_.push_back (p2.force_[0]);
1249 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
1250 return p1;
1253 vector<Real> page1_force;
1254 vector<Real> page2_force;
1256 // page1_penalty and page2_penalty store the penalties based
1257 // on min-systems-per-page and max-systems-per-page.
1258 vector<Real> page1_penalty;
1259 vector<Real> page2_penalty;
1261 // page1_status and page2_status keep track of whether the min-systems-per-page
1262 // and max-systems-per-page constraints are satisfied.
1263 vector<int> page1_status;
1264 vector<int> page2_status;
1266 Page_spacing page1 (page1_height, this);
1267 Page_spacing page2 (page2_height, this);
1268 int page1_line_count = 0;
1269 int page2_line_count = 0;
1271 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
1272 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
1273 page1_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1274 page2_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1275 page1_status.resize (cached_line_details_.size () - 1, 0);
1276 page2_status.resize (cached_line_details_.size () - 1, 0);
1278 /* find the page 1 and page 2 forces for each page-breaking position */
1279 for (vsize i = 0; i < page1_force.size (); i++)
1281 page1.append_system (cached_line_details_[i]);
1282 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1283 page1_line_count += cached_line_details_[i].compressed_nontitle_lines_count_;
1284 page2_line_count += cached_line_details_[cached_line_details_.size () - 1 - i].compressed_nontitle_lines_count_;
1286 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1287 page1_penalty[i] = line_count_penalty (page1_line_count);
1288 page1_status[i] = line_count_status (page1_line_count);
1290 if (ragged2)
1291 page2_force[page2_force.size () - 1 - i] =
1292 (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1293 else
1294 page2_force[page2_force.size () - 1 - i] = page2.force_;
1295 page2_penalty[page2_penalty.size () - 1 - i] = line_count_penalty (page2_line_count);
1296 page2_status[page2_penalty.size () - 1 - i] = line_count_status (page2_line_count);
1299 /* find the position that minimises the sum of the page forces */
1300 vsize best_sys_count = 1;
1301 Real best_demerits = infinity_f;
1302 for (vsize i = 0; i < page1_force.size (); i++)
1304 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1306 // NOTE: we treat max-systems-per-page and min-systems-per-page as soft
1307 // constraints. That is, we penalize harshly when they don't happen
1308 // but we don't completely reject.
1309 Real dem = f
1310 + page1_penalty[i] + page2_penalty[i]
1311 + cached_line_details_[i+1].page_penalty_
1312 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1313 if (dem < best_demerits)
1315 best_demerits = dem;
1316 best_sys_count = i+1;
1320 Page_spacing_result ret;
1321 ret.systems_per_page_.push_back (best_sys_count);
1322 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1323 ret.force_.push_back (page1_force[best_sys_count-1]);
1324 ret.force_.push_back (page2_force[best_sys_count-1]);
1325 ret.system_count_status_ = page1_status[best_sys_count-1] | page2_status[best_sys_count-1];
1326 ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
1327 + cached_line_details_.back ().page_penalty_
1328 + cached_line_details_.back ().turn_penalty_
1329 + page1_penalty[best_sys_count-1] + page2_penalty[best_sys_count-1];
1331 /* don't do finalize_spacing_result () because we are only an internal function */
1332 return ret;
1335 bool
1336 Page_breaking::all_lines_stretched (vsize configuration)
1338 cache_line_details (configuration);
1339 for (vsize i = 0; i < cached_line_details_.size (); i++)
1340 if (cached_line_details_[i].force_ < 0)
1341 return false;
1343 return true;
1346 Page_breaking::Line_division
1347 Page_breaking::current_configuration (vsize configuration_index) const
1349 return current_configurations_[configuration_index];
1352 bool
1353 Page_breaking::is_last () const
1355 return current_end_breakpoint_ == last_break_position ();
1358 bool
1359 Page_breaking::ends_score () const
1361 return breaks_[current_end_breakpoint_].score_ender_;
1364 vsize
1365 Page_breaking::last_break_position () const
1367 return breaks_.size () - 1;
1370 // This gives the minimum distance between the top of the
1371 // printable area (ie. the bottom of the top-margin) and
1372 // the extent box of the topmost system.
1373 Real
1374 Page_breaking::min_whitespace_at_top_of_page (Line_details const &line) const
1376 SCM first_system_spacing = book_->paper_->c_variable ("top-system-spacing");
1377 if (line.title_)
1378 first_system_spacing = book_->paper_->c_variable ("top-title-spacing");
1380 Real min_distance = -infinity_f;
1381 Real padding = 0;
1383 Page_layout_problem::read_spacing_spec (first_system_spacing,
1384 &min_distance,
1385 ly_symbol2scm ("minimum-distance"));
1386 Page_layout_problem::read_spacing_spec (first_system_spacing,
1387 &padding,
1388 ly_symbol2scm ("padding"));
1390 // FIXME: take into account the height of the header
1391 return max (0.0, max (padding, min_distance - line.extent_[UP]));
1394 Real
1395 Page_breaking::min_whitespace_at_bottom_of_page (Line_details const &line) const
1397 SCM last_system_spacing = book_->paper_->c_variable ("bottom-system-spacing");
1398 Real min_distance = -infinity_f;
1399 Real padding = 0;
1401 Page_layout_problem::read_spacing_spec (last_system_spacing,
1402 &min_distance,
1403 ly_symbol2scm ("minimum-distance"));
1404 Page_layout_problem::read_spacing_spec (last_system_spacing,
1405 &padding,
1406 ly_symbol2scm ("padding"));
1408 // FIXME: take into account the height of the footer
1409 return max (0.0, max (padding, min_distance + line.extent_[DOWN]));
1413 Page_breaking::orphan_penalty () const
1415 return orphan_penalty_;