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.
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
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).
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"
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"
89 /* for each forbidden page break, merge the systems around it into one
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];
103 We must account for the padding between the lines that we are compressing.
104 The padding values come from "old," which is the upper system here. Note
105 the meaning of tight-spacing: if a system has tight-spacing, then the padding
106 _before_ it is ignored.
109 if (!orig[i].tight_spacing_)
110 padding = orig[i].title_ ? old.title_padding_ : old.padding_;
112 // FIXME: double check these. Doesn't foo.piggyback (bar) mean
113 // that foo goes on top?
114 // TODO: break out a Line_details::piggyback from here?
115 compressed.shape_ = old.shape_.piggyback (orig[i].shape_, padding);
116 compressed.refpoint_extent_[UP] = old.refpoint_extent_[UP];
117 compressed.refpoint_extent_[DOWN] += compressed.shape_.rest_[UP] - old.shape_.rest_[UP];
118 compressed.space_ += old.space_;
119 compressed.inverse_hooke_ += old.inverse_hooke_;
121 compressed.compressed_lines_count_ = old.compressed_lines_count_ + 1;
122 compressed.compressed_nontitle_lines_count_ =
123 old.compressed_nontitle_lines_count_ + (compressed.title_ ? 0 : 1);
125 // compressed.title_ is true if and only if the first of its
126 // compressed lines was a title.
127 compressed.title_ = old.title_;
128 ret.back () = compressed;
132 ret.push_back (orig[i]);
133 ret.back ().force_ = 0;
139 /* translate the number of systems-per-page into something meaningful for
140 the uncompressed lines.
143 uncompress_solution (vector<vsize> const &systems_per_page,
144 vector<Line_details> const &compressed)
149 for (vsize i = 0; i < systems_per_page.size (); i++)
151 int compressed_count = 0;
152 for (vsize j = start_sys; j < start_sys + systems_per_page[i]; j++)
153 compressed_count += compressed[j].compressed_lines_count_ - 1;
155 ret.push_back (systems_per_page[i] + compressed_count);
156 start_sys += systems_per_page[i];
161 /* for Page_breaking, the start index (when we are dealing with the stuff
162 between a pair of breakpoints) refers to the break_ index of the end of
163 the previous page. So the system index of the start of the current page
164 could either be the same as the end of the previous page or one more than
167 /* Turn a break index into the sys index that starts the next page */
169 Page_breaking::next_system (Break_position const &break_pos) const
171 vsize sys = break_pos.system_spec_index_;
173 if (sys == VPOS) /* beginning of the book */
175 if (system_specs_[sys].pscore_ && !break_pos.score_ender_)
176 return sys; /* the score overflows the previous page */
177 return sys + 1; /* this page starts with a new System_spec */
180 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break, Prob_break_predicate prob_break)
184 paper_height_ = robust_scm2double (pb->paper_->c_variable ("paper-height"), 1.0);
185 ragged_ = to_boolean (pb->paper_->c_variable ("ragged-bottom"));
186 ragged_last_ = to_boolean (pb->paper_->c_variable ("ragged-last-bottom"));
187 systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("systems-per-page"), 0));
188 max_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("max-systems-per-page"), 0));
189 min_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("min-systems-per-page"), 0));
190 orphan_penalty_ = robust_scm2int (pb->paper_->c_variable ("orphan-penalty"), 100000);
192 if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_))
194 warning (_f ("ignoring min-systems-per-page and max-systems-per-page because systems-per-page was set"));
195 min_systems_per_page_ = max_systems_per_page_ = 0;
197 if (max_systems_per_page_ && min_systems_per_page_ > max_systems_per_page_)
199 warning (_f ("min-systems-per-page is larger than max-systems-per-page, ignoring both values"));
200 min_systems_per_page_ = max_systems_per_page_ = 0;
203 create_system_list ();
204 find_chunks_and_breaks (is_break, prob_break);
207 Page_breaking::~Page_breaking ()
212 Page_breaking::ragged () const
218 Page_breaking::ragged_last () const
224 Page_breaking::systems_per_page () const
226 return systems_per_page_;
230 Page_breaking::max_systems_per_page () const
232 if (systems_per_page_)
233 return systems_per_page_;
234 return max_systems_per_page_;
238 Page_breaking::min_systems_per_page () const
240 if (systems_per_page_)
241 return systems_per_page_;
242 return min_systems_per_page_;
246 Page_breaking::system_count () const
248 return system_count_;
252 Page_breaking::too_many_lines (int line_count) const
254 return max_systems_per_page () > 0 && line_count > max_systems_per_page ();
258 Page_breaking::too_few_lines (int line_count) const
260 return line_count < min_systems_per_page ();
264 Page_breaking::line_count_penalty (int line_count) const
266 if (too_many_lines (line_count))
267 return (line_count - max_systems_per_page ()) * TERRIBLE_SPACING_PENALTY;
268 if (too_few_lines (line_count))
269 return (min_systems_per_page () - line_count) * TERRIBLE_SPACING_PENALTY;
275 Page_breaking::line_count_status (int line_count) const
277 if (too_many_lines (line_count))
278 return SYSTEM_COUNT_TOO_MANY;
279 if (too_few_lines (line_count))
280 return SYSTEM_COUNT_TOO_FEW;
282 return SYSTEM_COUNT_OK;
285 /* translate indices into breaks_ into start-end parameters for the line breaker */
287 Page_breaking::line_breaker_args (vsize sys,
288 Break_position const &start,
289 Break_position const &end,
290 vsize *line_breaker_start,
291 vsize *line_breaker_end)
293 assert (system_specs_[sys].pscore_);
294 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
296 if (start.system_spec_index_ == sys)
297 *line_breaker_start = start.score_break_;
299 *line_breaker_start = 0;
301 if (end.system_spec_index_ == sys)
302 *line_breaker_end = end.score_break_;
304 *line_breaker_end = VPOS;
308 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
309 Line_division const &div)
311 vector<Break_position> chunks = chunk_list (start_break, end_break);
312 bool ignore_div = false;
313 if (chunks.size () != div.size () + 1)
315 programming_error ("did not find a valid page breaking configuration");
319 for (vsize i = 0; i + 1 < chunks.size (); i++)
321 vsize sys = next_system (chunks[i]);
322 if (system_specs_[sys].pscore_)
326 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
328 vector<Column_x_positions> pos = ignore_div
329 ? line_breaking_[sys].best_solution (start, end)
330 : line_breaking_[sys].solve (start, end, div[i]);
331 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
337 Page_breaking::systems ()
340 for (vsize sys = 0; sys < system_specs_.size (); sys++)
342 if (system_specs_[sys].pscore_)
344 system_specs_[sys].pscore_->root_system ()
345 ->do_break_substitution_and_fixup_refpoints ();
346 SCM lines = system_specs_[sys].pscore_->root_system ()
347 ->get_broken_system_grobs ();
348 ret = scm_cons (lines, ret);
350 else if (Prob *pb = system_specs_[sys].prob_)
352 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
356 return scm_append (scm_reverse (ret));
360 Page_breaking::make_page (int page_num, bool last) const
362 bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
363 SCM mod = scm_c_resolve_module ("scm page");
364 SCM make_page_scm = scm_c_module_lookup (mod, "make-page");
366 make_page_scm = scm_variable_ref (make_page_scm);
368 return scm_apply_0 (make_page_scm,
369 scm_list_n (book_->self_scm (),
370 ly_symbol2scm ("page-number"), scm_from_int (page_num),
371 ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
372 ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
376 // Returns the total height of the paper, including margins and
377 // space for the header/footer. This is an upper bound on
378 // page_height, and it doesn't depend on the current page.
380 Page_breaking::paper_height () const
382 return paper_height_;
386 Page_breaking::page_height (int page_num, bool last) const
388 // The caches allow us to store the page heights for any
389 // non-negative page numbers. We use a negative value in the
390 // cache to signal that that position has not yet been initialized.
391 // This means that we won't cache properly if page_num is negative or
392 // if calc_height returns a negative number. But that's likely to
393 // be rare, so it shouldn't affect performance.
394 vector<Real>& cache = last ? last_page_height_cache_ : page_height_cache_;
395 if (page_num >= 0 && (int) cache.size () > page_num && cache[page_num] >= 0)
396 return cache[page_num];
399 SCM mod = scm_c_resolve_module ("scm page");
400 SCM page = make_page (page_num, last);
401 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
402 calc_height = scm_variable_ref (calc_height);
404 SCM height_scm = scm_apply_1 (calc_height, page, SCM_EOL);
405 Real height = scm_to_double (height_scm);
409 if ((int) cache.size () <= page_num)
410 cache.resize (page_num + 1, -1);
411 cache[page_num] = height;
418 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
420 Break_position const &pos = breaks_[breakpoint];
422 if (pos.system_spec_index_ == VPOS)
424 if (system_specs_[pos.system_spec_index_].pscore_)
425 return pos.col_->get_property (str);
426 return system_specs_[pos.system_spec_index_].prob_->get_property (str);
430 Page_breaking::get_page_configuration (SCM systems, int page_num, bool ragged, bool last)
432 SCM dummy_page = make_page (page_num, last);
433 Page_layout_problem layout (book_, dummy_page, systems);
434 return scm_is_pair (systems) ? layout.solution (ragged) : SCM_EOL;
438 Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool last)
440 // Create a stencil for each system.
441 SCM paper_systems = SCM_EOL;
442 for (SCM s = scm_reverse (systems); scm_is_pair (s); s = scm_cdr (s))
444 SCM paper_system = scm_car (s);
445 if (Grob *g = unsmob_grob (scm_car (s)))
447 System *sys = dynamic_cast<System*> (g);
448 paper_system = sys->get_paper_system ();
451 paper_systems = scm_cons (paper_system, paper_systems);
454 // Create the page and draw it.
455 SCM page = make_page (page_num, last);
456 SCM page_module = scm_c_resolve_module ("scm page");
457 SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
458 page_stencil = scm_variable_ref (page_stencil);
460 Prob *p = unsmob_prob (page);
461 p->set_property ("lines", paper_systems);
462 p->set_property ("configuration", configuration);
463 scm_apply_1 (page_stencil, page, SCM_EOL);
469 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
471 if (scm_is_null (systems))
474 int first_page_number
475 = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
477 SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
478 if (label_page_table == SCM_UNDEFINED)
479 label_page_table = SCM_EOL;
481 // Build a list of (systems . configuration) pairs. Note that we lay out
482 // the staves and find the configurations before drawing anything. Some
483 // grobs (like tuplet brackets) look at their neighbours while drawing
484 // themselves. If this happens before the neighbouring staves have
485 // been laid out, bad side-effects could happen (in particular,
486 // Align_interface::align_to_ideal_distances might be called).
487 SCM systems_and_configs = SCM_EOL;
489 for (vsize i = 0; i < lines_per_page.size (); i++)
491 int page_num = i + first_page_number;
492 bool bookpart_last_page = (i == lines_per_page.size () - 1);
493 bool rag = ragged () || (bookpart_last_page && ragged_last ());
494 SCM line_count = scm_from_int (lines_per_page[i]);
495 SCM lines = scm_list_head (systems, line_count);
496 SCM config = get_page_configuration (lines, page_num, rag, bookpart_last_page);
498 systems_and_configs = scm_cons (scm_cons (lines, config), systems_and_configs);
499 systems = scm_list_tail (systems, line_count);
502 // Now it's safe to make the pages.
503 int page_num = first_page_number + lines_per_page.size () - 1;
504 for (SCM s = systems_and_configs; scm_is_pair (s); s = scm_cdr (s))
506 SCM lines = scm_caar (s);
507 SCM config = scm_cdar (s);
508 bool bookpart_last_page = (s == systems_and_configs);
509 SCM page = draw_page (lines, config, page_num, bookpart_last_page);
512 SCM page_num_scm = scm_from_int (page_num);
513 for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
515 SCM labels = SCM_EOL;
516 if (Grob * line = unsmob_grob (scm_car (l)))
518 System *system = dynamic_cast<System*> (line);
519 labels = system->get_property ("labels");
521 else if (Prob *prob = unsmob_prob (scm_car (l)))
522 labels = prob->get_property ("labels");
524 for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
525 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num_scm),
529 ret = scm_cons (page, ret);
532 book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
536 /* The page-turn-page-breaker needs to have a line-breaker between any two
537 columns with non-NULL page-turn-permission.
539 The optimal-breaker needs to have a line-breaker between any two columns
540 with page-break-permission = 'force.
542 By using a grob predicate, we can accommodate both of these uses.
545 Page_breaking::create_system_list ()
547 SCM specs = book_->get_system_specs ();
548 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
550 if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
552 system_specs_.push_back (System_spec (ps));
556 Prob *pb = unsmob_prob (scm_car (s));
560 system_specs_.push_back (System_spec (pb));
563 if (!system_specs_.size ())
564 system_specs_.push_back (System_spec ());
568 Page_breaking::find_chunks_and_breaks (Break_predicate is_break, Prob_break_predicate prob_is_break)
570 SCM force_sym = ly_symbol2scm ("force");
572 chunks_.push_back (Break_position ());
573 breaks_.push_back (Break_position ());
575 for (vsize i = 0; i < system_specs_.size (); i++)
577 if (system_specs_[i].pscore_)
579 vector<Grob*> cols = system_specs_[i].pscore_->root_system ()->used_columns ();
580 vector<Grob*> forced_line_break_cols;
582 SCM system_count = system_specs_[i].pscore_->layout ()->c_variable ("system-count");
583 if (scm_is_number (system_count))
585 // With system-count given, the line configuration for
586 // this score is fixed. We need to ensure that chunk
587 // boundaries only occur at line breaks.
588 Constrained_breaking breaking (system_specs_[i].pscore_);
589 vector<Line_details> details = breaking.line_details (0, VPOS, scm_to_int (system_count));
591 for (vsize j = 0; j < details.size (); j++)
592 forced_line_break_cols.push_back (details[j].last_column_);
595 int last_forced_line_break_idx = 0;
596 vsize forced_line_break_idx = 0;
597 vector<vsize> line_breaker_columns;
598 line_breaker_columns.push_back (0);
600 for (vsize j = 1; j < cols.size (); j++)
602 if (forced_line_break_cols.size ())
604 if (forced_line_break_idx >= forced_line_break_cols.size ()
605 || forced_line_break_cols[forced_line_break_idx] != cols[j])
608 forced_line_break_idx++;
611 bool last = (j == cols.size () - 1);
612 bool break_point = is_break && is_break (cols[j]);
613 bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
614 Break_position cur_pos = Break_position (i,
615 line_breaker_columns.size (),
619 // NOTE: even in the breaks_ list, forced_line_count_
620 // refers to the number of lines between a
621 // Break_position and the start of that /chunk/. This
622 // is needed for system_count_bounds to work correctly,
623 // since it mixes Break_positions from breaks_ and
625 if (scm_is_number (system_count))
626 cur_pos.forced_line_count_ = forced_line_break_idx - last_forced_line_break_idx;
628 if (break_point || (i == system_specs_.size () - 1 && last))
629 breaks_.push_back (cur_pos);
630 if (chunk_end || last)
632 chunks_.push_back (cur_pos);
633 last_forced_line_break_idx = forced_line_break_idx;
636 if ((break_point || chunk_end) && !last)
637 line_breaker_columns.push_back (j);
639 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
641 else if (system_specs_[i].prob_)
643 bool break_point = prob_is_break && prob_is_break (system_specs_[i].prob_);
644 if (break_point || i == system_specs_.size () - 1)
645 breaks_.push_back (Break_position (i));
647 chunks_.push_back (Break_position (i));
649 /* FIXME: shouldn't we push a Null_breaker or similar dummy
651 line_breaking_.push_back (Constrained_breaking (NULL));
656 vector<Break_position>
657 Page_breaking::chunk_list (vsize start_index, vsize end_index)
659 Break_position start = breaks_[start_index];
660 Break_position end = breaks_[end_index];
663 for (; i < chunks_.size () && chunks_[i] <= start; i++)
666 vector<Break_position> ret;
667 ret.push_back (start);
668 for (; i < chunks_.size () && chunks_[i] < end; i++)
669 ret.push_back (chunks_[i]);
675 Page_breaking::min_system_count (vsize start, vsize end)
677 vector<Break_position> chunks = chunk_list (start, end);
678 Line_division div = system_count_bounds (chunks, true);
681 for (vsize i = 0; i < div.size (); i++)
687 Page_breaking::max_system_count (vsize start, vsize end)
689 vector<Break_position> chunks = chunk_list (start, end);
690 Line_division div = system_count_bounds (chunks, false);
693 for (vsize i = 0; i < div.size (); i++)
698 Page_breaking::Line_division
699 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
702 assert (chunks.size () >= 2);
705 ret.resize (chunks.size () - 1, 1);
707 for (vsize i = 0; i + 1 < chunks.size (); i++)
709 vsize sys = next_system (chunks[i]);
711 if (chunks[i+1].forced_line_count_)
712 ret[i] = chunks[i+1].forced_line_count_;
713 else if (system_specs_[sys].pscore_)
717 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
719 ? line_breaking_[sys].min_system_count (start, end)
720 : line_breaking_[sys].max_system_count (start, end);
728 Page_breaking::set_current_breakpoints (vsize start,
731 Line_division lower_bound,
732 Line_division upper_bound)
734 system_count_ = system_count;
735 current_chunks_ = chunk_list (start, end);
736 current_start_breakpoint_ = start;
737 current_end_breakpoint_ = end;
738 clear_line_details_cache ();
740 if (!lower_bound.size ())
741 lower_bound = system_count_bounds (current_chunks_, true);
742 if (!upper_bound.size ())
743 upper_bound = system_count_bounds (current_chunks_, false);
745 assert (lower_bound.size () == current_chunks_.size () - 1);
746 assert (upper_bound.size () == current_chunks_.size () - 1);
748 Line_division work_in_progress;
749 current_configurations_.clear ();
750 line_divisions_rec (system_count,
755 /* we only consider a constant number of configurations. Otherwise,
756 this becomes slow when there are many small scores. The constant
757 5 is somewhat arbitrary. */
758 if (current_configurations_.size () > 5)
760 vector<pair<Real,vsize> > dems_and_indices;
762 for (vsize i = 0; i < current_configurations_.size (); i++)
764 cache_line_details (i);
766 for (vsize j = 0; j < cached_line_details_.size (); j++)
767 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
768 + cached_line_details_[j].break_penalty_;
770 dems_and_indices.push_back (pair<Real,vsize> (dem, i));
772 vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
774 vector<Line_division> best_5_configurations;
775 for (vsize i = 0; i < 5; i++)
776 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
778 clear_line_details_cache ();
779 current_configurations_ = best_5_configurations;
784 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
786 current_chunks_ = chunk_list (start, end);
787 current_start_breakpoint_ = start;
788 current_end_breakpoint_ = end;
789 clear_line_details_cache ();
793 for (vsize i = 0; i+1 < current_chunks_.size (); i++)
795 vsize sys = next_system (current_chunks_[i]);
797 if (current_chunks_[i+1].forced_line_count_)
798 div.push_back (current_chunks_[i+1].forced_line_count_);
799 else if (system_specs_[sys].pscore_)
801 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
802 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
807 system_count_ += div.back ();
809 current_configurations_.clear ();
810 current_configurations_.push_back (div);
814 Page_breaking::current_configuration_count () const
816 return current_configurations_.size ();
820 Page_breaking::cache_line_details (vsize configuration_index)
822 if (cached_configuration_index_ != configuration_index)
824 cached_configuration_index_ = configuration_index;
826 Line_division &div = current_configurations_[configuration_index];
827 uncompressed_line_details_.clear ();
828 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
830 vsize sys = next_system (current_chunks_[i]);
831 if (system_specs_[sys].pscore_)
835 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
837 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
838 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
842 assert (div[i] == 1);
843 uncompressed_line_details_.push_back (system_specs_[sys].prob_
844 ? Line_details (system_specs_[sys].prob_, book_->paper_)
848 cached_line_details_ = compress_lines (uncompressed_line_details_);
849 compute_line_heights ();
854 Page_breaking::clear_line_details_cache ()
856 cached_configuration_index_ = VPOS;
857 cached_line_details_.clear ();
858 uncompressed_line_details_.clear ();
862 Page_breaking::line_divisions_rec (vsize system_count,
863 Line_division const &min_sys,
864 Line_division const &max_sys,
865 Line_division *cur_division)
867 vsize my_index = cur_division->size ();
871 for (vsize i = my_index + 1; i < min_sys.size (); i++)
873 others_min += min_sys[i];
874 others_max += max_sys[i];
876 others_max = min (others_max, (int) system_count);
877 int real_min = max ((int) min_sys[my_index], (int) system_count - others_max);
878 int real_max = min ((int) max_sys[my_index], (int) system_count - others_min);
880 if (real_min > real_max || real_min <= 0)
882 /* this should never happen within a recursive call. If it happens
883 at all, it means that we were called with an unsolvable problem
884 and we should return an empty result */
885 assert (my_index == 0);
889 for (int i = real_min; i <= real_max; i++)
891 cur_division->push_back (i);
892 if (my_index == min_sys.size () - 1)
893 current_configurations_.push_back (*cur_division);
895 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
896 cur_division->pop_back ();
901 Page_breaking::compute_line_heights ()
903 Real prev_hanging = 0;
904 Real prev_hanging_begin = 0;
905 Real prev_hanging_rest = 0;
907 // refpoint_hanging is the y coordinate of the origin of this system.
908 // It may not be the same as refpoint_extent[UP], which is the
909 // refpoint of the first spaceable staff in this system.
910 Real prev_refpoint_hanging = 0;
911 for (vsize i = 0; i < cached_line_details_.size (); i++)
913 Line_details& cur = cached_line_details_[i];
914 Line_shape shape = cur.shape_;
915 Real a = shape.begin_[UP];
916 Real b = shape.rest_[UP];
917 bool title = cur.title_;
918 Real refpoint_hanging = max (prev_hanging_begin + a, prev_hanging_rest + b);
923 Line_details const& prev = cached_line_details_[i-1];
924 if (!cur.tight_spacing_)
926 ? prev.title_padding_
928 Real min_dist = title
929 ? prev.title_min_distance_
930 : prev.min_distance_;
931 refpoint_hanging = max (refpoint_hanging + padding,
932 prev_refpoint_hanging - prev.refpoint_extent_[DOWN]
933 + cur.refpoint_extent_[UP] + min_dist);
936 Real hanging_begin = refpoint_hanging - shape.begin_[DOWN];
937 Real hanging_rest = refpoint_hanging - shape.rest_[DOWN];
938 Real hanging = max (hanging_begin, hanging_rest);
939 cur.tallness_ = hanging - prev_hanging;
940 prev_hanging = hanging;
941 prev_hanging_begin = hanging_begin;
942 prev_hanging_rest = hanging_rest;
943 prev_refpoint_hanging = refpoint_hanging;
948 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
951 vsize page_starter = 0;
952 Real cur_rod_height = 0;
953 Real cur_spring_height = 0;
954 Real cur_page_height = page_height (first_page_num, false);
957 cache_line_details (configuration);
959 if (cached_line_details_.size ())
960 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[0]);
962 for (vsize i = 0; i < cached_line_details_.size (); i++)
964 Line_details const &cur = cached_line_details_[i];
965 Line_details const *const prev = (i > 0) ? &cached_line_details_[i-1] : 0;
967 if (cur_rod_height > 0)
968 ext_len = cur.tallness_;
970 ext_len = cur.full_height();
972 Real spring_len = (i > 0) ? prev->spring_length (cur) : 0;
973 Real next_rod_height = cur_rod_height + ext_len;
974 Real next_spring_height = cur_spring_height + spring_len;
975 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0)
976 + min_whitespace_at_bottom_of_page (cur);
977 int next_line_count = line_count + cur.compressed_nontitle_lines_count_;
979 if ((!too_few_lines (line_count) && (next_height > cur_page_height && cur_rod_height > 0))
980 || too_many_lines (next_line_count)
981 || (prev && prev->page_permission_ == ly_symbol2scm ("force")))
983 line_count = cur.compressed_nontitle_lines_count_;
984 cur_rod_height = cur.full_height();
985 cur_spring_height = 0;
988 cur_page_height = page_height (first_page_num + ret, false);
989 cur_page_height -= min_whitespace_at_top_of_page (cur);
995 cur_rod_height = next_rod_height;
996 cur_spring_height = next_spring_height;
997 line_count = next_line_count;
1001 /* there are two potential problems with the last page (because we didn't know
1002 it was the last page until after we managed to fit all the systems to it):
1003 - we are ragged-last but the last page has a compressed spring
1004 - the value returned by page_height (num, true) is smaller than the
1005 value returned by page_height (num, false) and it causes the page not to
1008 In either case, we just need to add one more page. This is because the last
1009 line will always fit on the extra page and by adding one more page to the
1010 end, the previous page is no longer the last page, so our previous
1011 calculations that treated it as a non-last page were ok.
1016 cur_page_height = page_height (first_page_num + ret - 1, true);
1017 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[page_starter]);
1018 cur_page_height -= min_whitespace_at_bottom_of_page (cached_line_details_.back ());
1020 Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
1021 if (!too_few_lines (line_count - cached_line_details_.back ().compressed_nontitle_lines_count_)
1022 && cur_height > cur_page_height
1023 /* don't increase the page count if the last page had only one system */
1024 && cur_rod_height > cached_line_details_.back ().full_height ())
1026 assert (ret <= cached_line_details_.size ());
1032 // If systems_per_page_ is positive, we don't really try to space on N pages;
1033 // we just put the requested number of systems on each page and penalize
1034 // if the result doesn't have N pages.
1036 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
1038 Page_spacing_result ret;
1040 if (systems_per_page_ > 0)
1042 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
1043 ret.demerits_ += (ret.force_.size () == n) ? 0 : BAD_SPACING_PENALTY;
1047 cache_line_details (configuration);
1048 bool valid_n = (n >= min_page_count (configuration, first_page_num)
1049 && n <= cached_line_details_.size ());
1052 programming_error ("number of pages is out of bounds");
1054 if (n == 1 && valid_n)
1055 ret = space_systems_on_1_page (cached_line_details_,
1056 page_height (first_page_num, is_last ()),
1057 ragged () || (is_last () && ragged_last ()));
1058 else if (n == 2 && valid_n)
1059 ret = space_systems_on_2_pages (configuration, first_page_num);
1062 Page_spacer ps (cached_line_details_, first_page_num, this);
1066 return finalize_spacing_result (configuration, ret);
1070 Page_breaking::blank_page_penalty () const
1075 penalty_sym = ly_symbol2scm ("blank-last-page-force");
1076 else if (ends_score ())
1077 penalty_sym = ly_symbol2scm ("blank-after-score-page-force");
1079 penalty_sym = ly_symbol2scm ("blank-page-force");
1081 Break_position const &pos = breaks_[current_end_breakpoint_];
1082 if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
1083 return robust_scm2double (ps->layout ()->lookup_variable (penalty_sym), 0.0);
1085 return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
1088 // If systems_per_page_ is positive, we don't really try to space on N
1089 // or N+1 pages; see the comment to space_systems_on_n_pages.
1091 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num,
1092 Real penalty_for_fewer_pages)
1094 Page_spacing_result n_res;
1095 Page_spacing_result m_res;
1097 if (systems_per_page_ > 0)
1099 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
1100 ret.demerits_ += (ret.force_.size () == n || ret.force_.size () == (n-1)) ? 0 : BAD_SPACING_PENALTY;
1104 cache_line_details (configuration);
1105 vsize min_p_count = min_page_count (configuration, first_page_num);
1106 bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
1109 programming_error ("both page counts are out of bounds");
1111 if (n == 1 && valid_n)
1113 bool rag = ragged () || (is_last () && ragged_last ());
1114 Real height = page_height (first_page_num, is_last ());
1116 if (1 >= min_p_count)
1117 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
1118 if (1 < cached_line_details_.size ())
1119 m_res = space_systems_on_2_pages (configuration, first_page_num);
1123 Page_spacer ps (cached_line_details_, first_page_num, this);
1125 if (n >= min_p_count || !valid_n)
1126 n_res = ps.solve (n);
1127 if (n < cached_line_details_.size () || !valid_n)
1128 m_res = ps.solve (n+1);
1131 m_res = finalize_spacing_result (configuration, m_res);
1132 n_res = finalize_spacing_result (configuration, n_res);
1134 Real page_spacing_weight = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1135 n_res.demerits_ += penalty_for_fewer_pages * page_spacing_weight;
1137 if (n_res.force_.size ())
1138 n_res.force_.back () += penalty_for_fewer_pages;
1140 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
1144 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
1146 if (systems_per_page_ > 0)
1147 return space_systems_with_fixed_number_per_page (configuration, first_page_num);
1149 cache_line_details (configuration);
1150 Page_spacer ps (cached_line_details_, first_page_num, this);
1152 return finalize_spacing_result (configuration, ps.solve ());
1156 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
1157 vsize first_page_num)
1159 Page_spacing_result res;
1160 Page_spacing space (page_height (first_page_num, false), this);
1163 vsize page_first_line = 0;
1165 cache_line_details (configuration);
1166 while (line < cached_line_details_.size ())
1170 space.resize (page_height (first_page_num + page, false));
1172 int system_count_on_this_page = 0;
1173 while (system_count_on_this_page < systems_per_page_
1174 && line < cached_line_details_.size ())
1176 Line_details const &cur_line = cached_line_details_[line];
1177 space.append_system (cur_line);
1178 system_count_on_this_page += cur_line.compressed_nontitle_lines_count_;
1181 if (cur_line.page_permission_ == ly_symbol2scm ("force"))
1185 res.systems_per_page_.push_back (line - page_first_line);
1187 res.force_.push_back (space.force_);
1188 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1189 if (system_count_on_this_page != systems_per_page_)
1191 res.penalty_ += abs (system_count_on_this_page - systems_per_page_) * TERRIBLE_SPACING_PENALTY;
1192 res.system_count_status_ |= ((system_count_on_this_page < systems_per_page_))
1193 ? SYSTEM_COUNT_TOO_FEW : SYSTEM_COUNT_TOO_MANY;
1196 page_first_line = line;
1199 /* Recalculate forces for the last page because we know now that is
1200 really the last page. */
1201 space.resize (page_height (first_page_num + page, true));
1202 res.force_.back () = space.force_;
1203 return finalize_spacing_result (configuration, res);
1207 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
1209 // TODO: add support for min/max-systems-per-page.
1210 Page_spacing_result res;
1212 vsize page_first_line = 0;
1213 Page_spacing space (page_height (first_page_num, false), this);
1215 cache_line_details (configuration);
1216 for (vsize line = 0; line < cached_line_details_.size (); line++)
1218 Real prev_force = space.force_;
1219 space.append_system (cached_line_details_[line]);
1220 if ((line > page_first_line)
1221 && (isinf (space.force_)
1223 && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
1225 res.systems_per_page_.push_back (line - page_first_line);
1226 res.force_.push_back (prev_force);
1227 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1229 space.resize (page_height (first_page_num + page, false));
1231 space.append_system (cached_line_details_[line]);
1232 page_first_line = line;
1235 if (line == cached_line_details_.size () - 1)
1237 /* This is the last line */
1238 /* When the last page height was computed, we did not know yet that it
1239 * was the last one. If the systems put on it don't fit anymore, the last
1240 * system is moved to a new page */
1241 space.resize (page_height (first_page_num + page, true));
1242 if ((line > page_first_line) && (isinf (space.force_)))
1244 res.systems_per_page_.push_back (line - page_first_line);
1245 res.force_.push_back (prev_force);
1246 /* the last page containing the last line */
1247 space.resize (page_height (first_page_num + page + 1, true));
1249 space.append_system (cached_line_details_[line]);
1250 res.systems_per_page_.push_back (1);
1251 res.force_.push_back (space.force_);
1252 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1253 res.penalty_ += cached_line_details_[line].page_penalty_;
1257 res.systems_per_page_.push_back (line + 1 - page_first_line);
1258 res.force_.push_back (space.force_);
1259 res.penalty_ += cached_line_details_[line].page_penalty_;
1263 return finalize_spacing_result (configuration, res);
1266 /* Calculate demerits and fix res.systems_per_page_ so that
1267 it refers to the original line numbers, not the ones given by compress_lines (). */
1269 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
1271 if (res.force_.empty ())
1274 cache_line_details (configuration);
1275 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
1277 Real line_force = 0;
1278 Real line_penalty = 0;
1279 Real page_demerits = res.penalty_;
1280 Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1282 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
1284 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
1285 line_penalty += uncompressed_line_details_[i].break_penalty_;
1288 for (vsize i = 0; i < res.force_.size (); i++)
1290 Real f = res.force_[i];
1292 page_demerits += min(f*f, BAD_SPACING_PENALTY);
1295 /* for a while we tried averaging page and line forces across pages instead
1296 of summing them, but it caused a problem: if there is a single page
1297 with a very bad page force (for example because of a forced page break),
1298 the page breaker will put in a _lot_ of pages so that the bad force
1299 becomes averaged out over many pages. */
1300 res.demerits_ = line_force + line_penalty + page_demerits * page_weighting;
1305 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
1306 are by far the most common cases, we have special functions for them.
1308 space_systems_on_1_page has a different calling convention than most of the
1309 space_systems functions. This is because space_systems_on_1_page is (unlike
1310 the other space_systems functions) sometimes called on subsets of a full
1313 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
1315 Page_spacing space (page_height, this);
1316 Page_spacing_result ret;
1319 for (vsize i = 0; i < lines.size (); i++)
1321 space.append_system (lines[i]);
1322 line_count += lines[i].compressed_nontitle_lines_count_;
1325 ret.systems_per_page_.push_back (lines.size ());
1326 ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
1327 ret.penalty_ = line_count_penalty (line_count) + lines.back ().page_penalty_ + lines.back ().turn_penalty_;
1328 ret.system_count_status_ |= line_count_status (line_count);
1330 /* don't do finalize_spacing_result () because we are only an internal function */
1335 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
1337 Real page1_height = page_height (first_page_num, false);
1338 Real page2_height = page_height (first_page_num + 1, is_last ());
1339 bool ragged1 = ragged ();
1340 bool ragged2 = ragged () || (is_last () && ragged_last ());
1342 /* if there is a forced break, this reduces to 2 1-page problems */
1343 cache_line_details (configuration);
1344 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
1345 if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
1347 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
1348 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
1349 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
1350 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
1352 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
1353 p1.force_.push_back (p2.force_[0]);
1354 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
1355 p1.system_count_status_ |= p2.system_count_status_;
1359 vector<Real> page1_force;
1360 vector<Real> page2_force;
1362 // page1_penalty and page2_penalty store the penalties based
1363 // on min-systems-per-page and max-systems-per-page.
1364 vector<Real> page1_penalty;
1365 vector<Real> page2_penalty;
1367 // page1_status and page2_status keep track of whether the min-systems-per-page
1368 // and max-systems-per-page constraints are satisfied.
1369 vector<int> page1_status;
1370 vector<int> page2_status;
1372 Page_spacing page1 (page1_height, this);
1373 Page_spacing page2 (page2_height, this);
1374 int page1_line_count = 0;
1375 int page2_line_count = 0;
1377 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
1378 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
1379 page1_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1380 page2_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1381 page1_status.resize (cached_line_details_.size () - 1, 0);
1382 page2_status.resize (cached_line_details_.size () - 1, 0);
1384 /* find the page 1 and page 2 forces for each page-breaking position */
1385 for (vsize i = 0; i < page1_force.size (); i++)
1387 page1.append_system (cached_line_details_[i]);
1388 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1389 page1_line_count += cached_line_details_[i].compressed_nontitle_lines_count_;
1390 page2_line_count += cached_line_details_[cached_line_details_.size () - 1 - i].compressed_nontitle_lines_count_;
1392 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1393 page1_penalty[i] = line_count_penalty (page1_line_count);
1394 page1_status[i] = line_count_status (page1_line_count);
1397 page2_force[page2_force.size () - 1 - i] =
1398 (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1400 page2_force[page2_force.size () - 1 - i] = page2.force_;
1401 page2_penalty[page2_penalty.size () - 1 - i] = line_count_penalty (page2_line_count);
1402 page2_status[page2_penalty.size () - 1 - i] = line_count_status (page2_line_count);
1405 /* find the position that minimises the sum of the page forces */
1406 vsize best_sys_count = 1;
1407 Real best_demerits = infinity_f;
1408 for (vsize i = 0; i < page1_force.size (); i++)
1410 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1412 // NOTE: we treat max-systems-per-page and min-systems-per-page as soft
1413 // constraints. That is, we penalize harshly when they don't happen
1414 // but we don't completely reject.
1416 + page1_penalty[i] + page2_penalty[i]
1417 + cached_line_details_[i+1].page_penalty_
1418 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1419 if (dem < best_demerits)
1421 best_demerits = dem;
1422 best_sys_count = i+1;
1426 Page_spacing_result ret;
1427 ret.systems_per_page_.push_back (best_sys_count);
1428 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1429 ret.force_.push_back (page1_force[best_sys_count-1]);
1430 ret.force_.push_back (page2_force[best_sys_count-1]);
1431 ret.system_count_status_ = page1_status[best_sys_count-1] | page2_status[best_sys_count-1];
1432 ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
1433 + cached_line_details_.back ().page_penalty_
1434 + cached_line_details_.back ().turn_penalty_
1435 + page1_penalty[best_sys_count-1] + page2_penalty[best_sys_count-1];
1437 /* don't do finalize_spacing_result () because we are only an internal function */
1442 Page_breaking::all_lines_stretched (vsize configuration)
1444 cache_line_details (configuration);
1445 for (vsize i = 0; i < cached_line_details_.size (); i++)
1446 if (cached_line_details_[i].force_ < 0)
1452 Page_breaking::Line_division
1453 Page_breaking::current_configuration (vsize configuration_index) const
1455 return current_configurations_[configuration_index];
1459 Page_breaking::is_last () const
1461 return current_end_breakpoint_ == last_break_position ();
1465 Page_breaking::ends_score () const
1467 return breaks_[current_end_breakpoint_].score_ender_;
1471 Page_breaking::last_break_position () const
1473 return breaks_.size () - 1;
1476 // This gives the minimum distance between the top of the
1477 // printable area (ie. the bottom of the top-margin) and
1478 // the extent box of the topmost system.
1480 Page_breaking::min_whitespace_at_top_of_page (Line_details const &line) const
1482 SCM first_system_spacing = book_->paper_->c_variable ("top-system-spacing");
1484 first_system_spacing = book_->paper_->c_variable ("top-markup-spacing");
1486 Real min_distance = -infinity_f;
1489 Page_layout_problem::read_spacing_spec (first_system_spacing,
1491 ly_symbol2scm ("minimum-distance"));
1492 Page_layout_problem::read_spacing_spec (first_system_spacing,
1494 ly_symbol2scm ("padding"));
1496 // FIXME: take into account the height of the header
1497 Real translate = max (line.shape_.begin_[UP], line.shape_.rest_[UP]);
1498 return max (0.0, max (padding, min_distance - translate));
1502 Page_breaking::min_whitespace_at_bottom_of_page (Line_details const &line) const
1504 SCM last_system_spacing = book_->paper_->c_variable ("last-bottom-spacing");
1505 Real min_distance = -infinity_f;
1508 Page_layout_problem::read_spacing_spec (last_system_spacing,
1510 ly_symbol2scm ("minimum-distance"));
1511 Page_layout_problem::read_spacing_spec (last_system_spacing,
1513 ly_symbol2scm ("padding"));
1515 // FIXME: take into account the height of the footer
1516 Real translate = min (line.shape_.begin_[DOWN], line.shape_.rest_[DOWN]);
1517 return max (0.0, max (padding, min_distance + translate));
1521 Page_breaking::orphan_penalty () const
1523 return orphan_penalty_;