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 return max_systems_per_page_;
236 Page_breaking::min_systems_per_page () const
238 return min_systems_per_page_;
242 Page_breaking::system_count () const
244 return system_count_;
248 Page_breaking::too_many_lines (int line_count) const
250 return max_systems_per_page () > 0 && line_count > max_systems_per_page ();
254 Page_breaking::too_few_lines (int line_count) const
256 return line_count < min_systems_per_page ();
260 Page_breaking::line_count_penalty (int line_count) const
262 if (too_many_lines (line_count))
263 return (line_count - max_systems_per_page ()) * TERRIBLE_SPACING_PENALTY;
264 if (too_few_lines (line_count))
265 return (min_systems_per_page () - line_count) * TERRIBLE_SPACING_PENALTY;
271 Page_breaking::line_count_status (int line_count) const
273 if (too_many_lines (line_count))
274 return SYSTEM_COUNT_TOO_MANY;
275 if (too_few_lines (line_count))
276 return SYSTEM_COUNT_TOO_FEW;
278 return SYSTEM_COUNT_OK;
281 /* translate indices into breaks_ into start-end parameters for the line breaker */
283 Page_breaking::line_breaker_args (vsize sys,
284 Break_position const &start,
285 Break_position const &end,
286 vsize *line_breaker_start,
287 vsize *line_breaker_end)
289 assert (system_specs_[sys].pscore_);
290 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
292 if (start.system_spec_index_ == sys)
293 *line_breaker_start = start.score_break_;
295 *line_breaker_start = 0;
297 if (end.system_spec_index_ == sys)
298 *line_breaker_end = end.score_break_;
300 *line_breaker_end = VPOS;
304 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
305 Line_division const &div)
307 vector<Break_position> chunks = chunk_list (start_break, end_break);
308 bool ignore_div = false;
309 if (chunks.size () != div.size () + 1)
311 programming_error ("did not find a valid page breaking configuration");
315 for (vsize i = 0; i + 1 < chunks.size (); i++)
317 vsize sys = next_system (chunks[i]);
318 if (system_specs_[sys].pscore_)
322 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
324 vector<Column_x_positions> pos = ignore_div
325 ? line_breaking_[sys].best_solution (start, end)
326 : line_breaking_[sys].solve (start, end, div[i]);
327 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
333 Page_breaking::systems ()
336 for (vsize sys = 0; sys < system_specs_.size (); sys++)
338 if (system_specs_[sys].pscore_)
340 system_specs_[sys].pscore_->root_system ()
341 ->do_break_substitution_and_fixup_refpoints ();
342 SCM lines = system_specs_[sys].pscore_->root_system ()
343 ->get_broken_system_grobs ();
344 ret = scm_cons (lines, ret);
348 Prob *pb = system_specs_[sys].prob_;
349 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
353 return scm_append (scm_reverse (ret));
357 Page_breaking::make_page (int page_num, bool last) const
359 bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
360 SCM mod = scm_c_resolve_module ("scm page");
361 SCM make_page_scm = scm_c_module_lookup (mod, "make-page");
363 make_page_scm = scm_variable_ref (make_page_scm);
365 return scm_apply_0 (make_page_scm,
366 scm_list_n (book_->self_scm (),
367 ly_symbol2scm ("page-number"), scm_from_int (page_num),
368 ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
369 ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
373 // Returns the total height of the paper, including margins and
374 // space for the header/footer. This is an upper bound on
375 // page_height, and it doesn't depend on the current page.
377 Page_breaking::paper_height () const
379 return paper_height_;
383 Page_breaking::page_height (int page_num, bool last) const
385 // The caches allow us to store the page heights for any
386 // non-negative page numbers. We use a negative value in the
387 // cache to signal that that position has not yet been initialized.
388 // This means that we won't cache properly if page_num is negative or
389 // if calc_height returns a negative number. But that's likely to
390 // be rare, so it shouldn't affect performance.
391 vector<Real>& cache = last ? last_page_height_cache_ : page_height_cache_;
392 if (page_num >= 0 && (int) cache.size () > page_num && cache[page_num] >= 0)
393 return cache[page_num];
396 SCM mod = scm_c_resolve_module ("scm page");
397 SCM page = make_page (page_num, last);
398 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
399 calc_height = scm_variable_ref (calc_height);
401 SCM height_scm = scm_apply_1 (calc_height, page, SCM_EOL);
402 Real height = scm_to_double (height_scm);
406 if ((int) cache.size () <= page_num)
407 cache.resize (page_num + 1, -1);
408 cache[page_num] = height;
415 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
417 Break_position const &pos = breaks_[breakpoint];
419 if (pos.system_spec_index_ == VPOS)
421 if (system_specs_[pos.system_spec_index_].pscore_)
422 return pos.col_->get_property (str);
423 return system_specs_[pos.system_spec_index_].prob_->get_property (str);
427 Page_breaking::get_page_configuration (SCM systems, int page_num, bool ragged, bool last)
429 SCM dummy_page = make_page (page_num, last);
430 Page_layout_problem layout (book_, dummy_page, systems);
431 return scm_is_pair (systems) ? layout.solution (ragged) : SCM_EOL;
435 Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool last)
437 // Create a stencil for each system.
438 SCM paper_systems = SCM_EOL;
439 for (SCM s = scm_reverse (systems); scm_is_pair (s); s = scm_cdr (s))
441 SCM paper_system = scm_car (s);
442 if (Grob *g = unsmob_grob (scm_car (s)))
444 System *sys = dynamic_cast<System*> (g);
445 paper_system = sys->get_paper_system ();
448 paper_systems = scm_cons (paper_system, paper_systems);
451 // Create the page and draw it.
452 SCM page = make_page (page_num, last);
453 SCM page_module = scm_c_resolve_module ("scm page");
454 SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
455 page_stencil = scm_variable_ref (page_stencil);
457 Prob *p = unsmob_prob (page);
458 p->set_property ("lines", paper_systems);
459 p->set_property ("configuration", configuration);
460 scm_apply_1 (page_stencil, page, SCM_EOL);
466 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
468 int first_page_number
469 = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
471 SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
472 if (label_page_table == SCM_UNDEFINED)
473 label_page_table = SCM_EOL;
475 // Build a list of (systems . configuration) pairs. Note that we lay out
476 // the staves and find the configurations before drawing anything. Some
477 // grobs (like tuplet brackets) look at their neighbours while drawing
478 // themselves. If this happens before the neighbouring staves have
479 // been laid out, bad side-effects could happen (in particular,
480 // Align_interface::align_to_ideal_distances might be called).
481 SCM systems_and_configs = SCM_EOL;
483 for (vsize i = 0; i < lines_per_page.size (); i++)
485 int page_num = i + first_page_number;
486 bool bookpart_last_page = (i == lines_per_page.size () - 1);
487 bool rag = ragged () || (bookpart_last_page && ragged_last ());
488 SCM line_count = scm_from_int (lines_per_page[i]);
489 SCM lines = scm_list_head (systems, line_count);
490 SCM config = get_page_configuration (lines, page_num, rag, bookpart_last_page);
492 systems_and_configs = scm_cons (scm_cons (lines, config), systems_and_configs);
493 systems = scm_list_tail (systems, line_count);
496 // Now it's safe to make the pages.
497 int page_num = first_page_number + lines_per_page.size () - 1;
498 for (SCM s = systems_and_configs; scm_is_pair (s); s = scm_cdr (s))
500 SCM lines = scm_caar (s);
501 SCM config = scm_cdar (s);
502 bool bookpart_last_page = (s == systems_and_configs);
503 SCM page = draw_page (lines, config, page_num, bookpart_last_page);
506 SCM page_num_scm = scm_from_int (page_num);
507 for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
509 SCM labels = SCM_EOL;
510 if (Grob * line = unsmob_grob (scm_car (l)))
512 System *system = dynamic_cast<System*> (line);
513 labels = system->get_property ("labels");
515 else if (Prob *prob = unsmob_prob (scm_car (l)))
516 labels = prob->get_property ("labels");
518 for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
519 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num_scm),
523 ret = scm_cons (page, ret);
526 book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
530 /* The page-turn-page-breaker needs to have a line-breaker between any two
531 columns with non-NULL page-turn-permission.
533 The optimal-breaker needs to have a line-breaker between any two columns
534 with page-break-permission = 'force.
536 By using a grob predicate, we can accommodate both of these uses.
539 Page_breaking::create_system_list ()
541 SCM specs = book_->get_system_specs ();
542 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
544 if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
546 system_specs_.push_back (System_spec (ps));
550 Prob *pb = unsmob_prob (scm_car (s));
554 system_specs_.push_back (System_spec (pb));
560 Page_breaking::find_chunks_and_breaks (Break_predicate is_break, Prob_break_predicate prob_is_break)
562 SCM force_sym = ly_symbol2scm ("force");
564 chunks_.push_back (Break_position ());
565 breaks_.push_back (Break_position ());
567 for (vsize i = 0; i < system_specs_.size (); i++)
569 if (system_specs_[i].pscore_)
571 vector<Grob*> cols = system_specs_[i].pscore_->root_system ()->used_columns ();
572 vector<Grob*> forced_line_break_cols;
574 SCM system_count = system_specs_[i].pscore_->layout ()->c_variable ("system-count");
575 if (scm_is_number (system_count))
577 // With system-count given, the line configuration for
578 // this score is fixed. We need to ensure that chunk
579 // boundaries only occur at line breaks.
580 Constrained_breaking breaking (system_specs_[i].pscore_);
581 vector<Line_details> details = breaking.line_details (0, VPOS, scm_to_int (system_count));
583 for (vsize j = 0; j < details.size (); j++)
584 forced_line_break_cols.push_back (details[j].last_column_);
587 int last_forced_line_break_idx = 0;
588 vsize forced_line_break_idx = 0;
589 vector<vsize> line_breaker_columns;
590 line_breaker_columns.push_back (0);
592 for (vsize j = 1; j < cols.size (); j++)
594 if (forced_line_break_cols.size ())
596 if (forced_line_break_idx >= forced_line_break_cols.size ()
597 || forced_line_break_cols[forced_line_break_idx] != cols[j])
600 forced_line_break_idx++;
603 bool last = (j == cols.size () - 1);
604 bool break_point = is_break && is_break (cols[j]);
605 bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
606 Break_position cur_pos = Break_position (i,
607 line_breaker_columns.size (),
611 // NOTE: even in the breaks_ list, forced_line_count_
612 // refers to the number of lines between a
613 // Break_position and the start of that /chunk/. This
614 // is needed for system_count_bounds to work correctly,
615 // since it mixes Break_positions from breaks_ and
617 if (scm_is_number (system_count))
618 cur_pos.forced_line_count_ = forced_line_break_idx - last_forced_line_break_idx;
620 if (break_point || (i == system_specs_.size () - 1 && last))
621 breaks_.push_back (cur_pos);
622 if (chunk_end || last)
624 chunks_.push_back (cur_pos);
625 last_forced_line_break_idx = forced_line_break_idx;
628 if ((break_point || chunk_end) && !last)
629 line_breaker_columns.push_back (j);
631 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
635 bool break_point = prob_is_break && prob_is_break (system_specs_[i].prob_);
636 if (break_point || i == system_specs_.size () - 1)
637 breaks_.push_back (Break_position (i));
639 chunks_.push_back (Break_position (i));
641 /* FIXME: shouldn't we push a Null_breaker or similar dummy
643 line_breaking_.push_back (Constrained_breaking (NULL));
648 vector<Break_position>
649 Page_breaking::chunk_list (vsize start_index, vsize end_index)
651 Break_position start = breaks_[start_index];
652 Break_position end = breaks_[end_index];
655 for (; i < chunks_.size () && chunks_[i] <= start; i++)
658 vector<Break_position> ret;
659 ret.push_back (start);
660 for (; i < chunks_.size () && chunks_[i] < end; i++)
661 ret.push_back (chunks_[i]);
667 Page_breaking::min_system_count (vsize start, vsize end)
669 vector<Break_position> chunks = chunk_list (start, end);
670 Line_division div = system_count_bounds (chunks, true);
673 for (vsize i = 0; i < div.size (); i++)
679 Page_breaking::max_system_count (vsize start, vsize end)
681 vector<Break_position> chunks = chunk_list (start, end);
682 Line_division div = system_count_bounds (chunks, false);
685 for (vsize i = 0; i < div.size (); i++)
690 Page_breaking::Line_division
691 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
694 assert (chunks.size () >= 2);
697 ret.resize (chunks.size () - 1, 1);
699 for (vsize i = 0; i + 1 < chunks.size (); i++)
701 vsize sys = next_system (chunks[i]);
703 if (chunks[i+1].forced_line_count_)
704 ret[i] = chunks[i+1].forced_line_count_;
705 else if (system_specs_[sys].pscore_)
709 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
711 ? line_breaking_[sys].min_system_count (start, end)
712 : line_breaking_[sys].max_system_count (start, end);
720 Page_breaking::set_current_breakpoints (vsize start,
723 Line_division lower_bound,
724 Line_division upper_bound)
726 system_count_ = system_count;
727 current_chunks_ = chunk_list (start, end);
728 current_start_breakpoint_ = start;
729 current_end_breakpoint_ = end;
730 clear_line_details_cache ();
732 if (!lower_bound.size ())
733 lower_bound = system_count_bounds (current_chunks_, true);
734 if (!upper_bound.size ())
735 upper_bound = system_count_bounds (current_chunks_, false);
737 assert (lower_bound.size () == current_chunks_.size () - 1);
738 assert (upper_bound.size () == current_chunks_.size () - 1);
740 Line_division work_in_progress;
741 current_configurations_.clear ();
742 line_divisions_rec (system_count,
747 /* we only consider a constant number of configurations. Otherwise,
748 this becomes slow when there are many small scores. The constant
749 5 is somewhat arbitrary. */
750 if (current_configurations_.size () > 5)
752 vector<pair<Real,vsize> > dems_and_indices;
754 for (vsize i = 0; i < current_configurations_.size (); i++)
756 cache_line_details (i);
758 for (vsize j = 0; j < cached_line_details_.size (); j++)
759 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
760 + cached_line_details_[j].break_penalty_;
762 dems_and_indices.push_back (pair<Real,vsize> (dem, i));
764 vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
766 vector<Line_division> best_5_configurations;
767 for (vsize i = 0; i < 5; i++)
768 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
770 clear_line_details_cache ();
771 current_configurations_ = best_5_configurations;
776 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
778 current_chunks_ = chunk_list (start, end);
779 current_start_breakpoint_ = start;
780 current_end_breakpoint_ = end;
781 clear_line_details_cache ();
785 for (vsize i = 0; i+1 < current_chunks_.size (); i++)
787 vsize sys = next_system (current_chunks_[i]);
789 if (current_chunks_[i+1].forced_line_count_)
790 div.push_back (current_chunks_[i+1].forced_line_count_);
791 else if (system_specs_[sys].pscore_)
793 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
794 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
799 system_count_ += div.back ();
801 current_configurations_.clear ();
802 current_configurations_.push_back (div);
806 Page_breaking::current_configuration_count () const
808 return current_configurations_.size ();
812 Page_breaking::cache_line_details (vsize configuration_index)
814 if (cached_configuration_index_ != configuration_index)
816 cached_configuration_index_ = configuration_index;
818 Line_division &div = current_configurations_[configuration_index];
819 uncompressed_line_details_.clear ();
820 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
822 vsize sys = next_system (current_chunks_[i]);
823 if (system_specs_[sys].pscore_)
827 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
829 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
830 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
834 assert (div[i] == 1);
835 uncompressed_line_details_.push_back (Line_details (system_specs_[sys].prob_, book_->paper_));
838 cached_line_details_ = compress_lines (uncompressed_line_details_);
839 compute_line_heights ();
844 Page_breaking::clear_line_details_cache ()
846 cached_configuration_index_ = VPOS;
847 cached_line_details_.clear ();
848 uncompressed_line_details_.clear ();
852 Page_breaking::line_divisions_rec (vsize system_count,
853 Line_division const &min_sys,
854 Line_division const &max_sys,
855 Line_division *cur_division)
857 vsize my_index = cur_division->size ();
861 for (vsize i = my_index + 1; i < min_sys.size (); i++)
863 others_min += min_sys[i];
864 others_max += max_sys[i];
866 others_max = min (others_max, (int) system_count);
867 int real_min = max ((int) min_sys[my_index], (int) system_count - others_max);
868 int real_max = min ((int) max_sys[my_index], (int) system_count - others_min);
870 if (real_min > real_max || real_min <= 0)
872 /* this should never happen within a recursive call. If it happens
873 at all, it means that we were called with an unsolvable problem
874 and we should return an empty result */
875 assert (my_index == 0);
879 for (int i = real_min; i <= real_max; i++)
881 cur_division->push_back (i);
882 if (my_index == min_sys.size () - 1)
883 current_configurations_.push_back (*cur_division);
885 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
886 cur_division->pop_back ();
891 Page_breaking::compute_line_heights ()
893 Real prev_hanging = 0;
894 Real prev_hanging_begin = 0;
895 Real prev_hanging_rest = 0;
897 // refpoint_hanging is the y coordinate of the origin of this system.
898 // It may not be the same as refpoint_extent[UP], which is the
899 // refpoint of the first spaceable staff in this system.
900 Real prev_refpoint_hanging = 0;
901 for (vsize i = 0; i < cached_line_details_.size (); i++)
903 Line_details& cur = cached_line_details_[i];
904 Line_shape shape = cur.shape_;
905 Real a = shape.begin_[UP];
906 Real b = shape.rest_[UP];
907 bool title = cur.title_;
908 Real refpoint_hanging = max (prev_hanging_begin + a, prev_hanging_rest + b);
913 Line_details const& prev = cached_line_details_[i-1];
914 if (!cur.tight_spacing_)
916 ? prev.title_padding_
918 Real min_dist = title
919 ? prev.title_min_distance_
920 : prev.min_distance_;
921 refpoint_hanging = max (refpoint_hanging + padding,
922 prev_refpoint_hanging - prev.refpoint_extent_[DOWN]
923 + cur.refpoint_extent_[UP] + min_dist);
926 Real hanging_begin = refpoint_hanging - shape.begin_[DOWN];
927 Real hanging_rest = refpoint_hanging - shape.rest_[DOWN];
928 Real hanging = max (hanging_begin, hanging_rest);
929 cur.tallness_ = hanging - prev_hanging;
930 prev_hanging = hanging;
931 prev_hanging_begin = hanging_begin;
932 prev_hanging_rest = hanging_rest;
933 prev_refpoint_hanging = refpoint_hanging;
938 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
941 vsize page_starter = 0;
942 Real cur_rod_height = 0;
943 Real cur_spring_height = 0;
944 Real cur_page_height = page_height (first_page_num, false);
947 cache_line_details (configuration);
949 if (cached_line_details_.size ())
950 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[0]);
952 for (vsize i = 0; i < cached_line_details_.size (); i++)
955 if (cur_rod_height > 0)
956 ext_len = cached_line_details_[i].tallness_;
958 ext_len = cached_line_details_[i].full_height();
960 Real next_rod_height = cur_rod_height + ext_len;
961 Real next_spring_height = cur_spring_height + cached_line_details_[i].space_;
962 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0)
963 + min_whitespace_at_bottom_of_page (cached_line_details_[i]);
964 int next_line_count = line_count + cached_line_details_[i].compressed_nontitle_lines_count_;
966 if ((!too_few_lines (line_count) && (next_height > cur_page_height && cur_rod_height > 0))
967 || too_many_lines (next_line_count)
969 && cached_line_details_[i-1].page_permission_ == ly_symbol2scm ("force")))
971 line_count = cached_line_details_[i].compressed_nontitle_lines_count_;
972 cur_rod_height = cached_line_details_[i].full_height();
973 cur_spring_height = cached_line_details_[i].space_;
976 cur_page_height = page_height (first_page_num + ret, false);
977 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[i]);
983 cur_rod_height = next_rod_height;
984 cur_spring_height = next_spring_height;
985 line_count = next_line_count;
989 /* there are two potential problems with the last page (because we didn't know
990 it was the last page until after we managed to fit all the systems to it):
991 - we are ragged-last but the last page has a compressed spring
992 - the value returned by page_height (num, true) is smaller than the
993 value returned by page_height (num, false) and it causes the page not to
996 In either case, we just need to add one more page. This is because the last
997 line will always fit on the extra page and by adding one more page to the
998 end, the previous page is no longer the last page, so our previous
999 calculations that treated it as a non-last page were ok.
1002 cur_page_height = page_height (first_page_num + ret - 1, true);
1003 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[page_starter]);
1004 cur_page_height -= min_whitespace_at_bottom_of_page (cached_line_details_.back ());
1006 Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
1007 if (!too_few_lines (line_count - cached_line_details_.back ().compressed_nontitle_lines_count_)
1008 && cur_height > cur_page_height
1009 /* don't increase the page count if the last page had only one system */
1010 && cur_rod_height > cached_line_details_.back ().full_height ())
1013 assert (ret <= cached_line_details_.size ());
1017 // If systems_per_page_ is positive, we don't really try to space on N pages;
1018 // we just put the requested number of systems on each page and penalize
1019 // if the result doesn't have N pages.
1021 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
1023 Page_spacing_result ret;
1025 if (systems_per_page_ > 0)
1027 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
1028 ret.demerits_ += (ret.force_.size () == n) ? 0 : BAD_SPACING_PENALTY;
1032 cache_line_details (configuration);
1033 bool valid_n = (n >= min_page_count (configuration, first_page_num)
1034 && n <= cached_line_details_.size ());
1037 programming_error ("number of pages is out of bounds");
1039 if (n == 1 && valid_n)
1040 ret = space_systems_on_1_page (cached_line_details_,
1041 page_height (first_page_num, is_last ()),
1042 ragged () || (is_last () && ragged_last ()));
1043 else if (n == 2 && valid_n)
1044 ret = space_systems_on_2_pages (configuration, first_page_num);
1047 Page_spacer ps (cached_line_details_, first_page_num, this);
1051 return finalize_spacing_result (configuration, ret);
1055 Page_breaking::blank_page_penalty () const
1060 penalty_sym = ly_symbol2scm ("blank-last-page-force");
1061 else if (ends_score ())
1062 penalty_sym = ly_symbol2scm ("blank-after-score-page-force");
1064 penalty_sym = ly_symbol2scm ("blank-page-force");
1066 Break_position const &pos = breaks_[current_end_breakpoint_];
1067 if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
1068 return robust_scm2double (ps->layout ()->lookup_variable (penalty_sym), 0.0);
1070 return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
1073 // If systems_per_page_ is positive, we don't really try to space on N
1074 // or N+1 pages; see the comment to space_systems_on_n_pages.
1076 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num,
1077 Real penalty_for_fewer_pages)
1079 Page_spacing_result n_res;
1080 Page_spacing_result m_res;
1082 if (systems_per_page_ > 0)
1084 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
1085 ret.demerits_ += (ret.force_.size () == n || ret.force_.size () == (n-1)) ? 0 : BAD_SPACING_PENALTY;
1089 cache_line_details (configuration);
1090 vsize min_p_count = min_page_count (configuration, first_page_num);
1091 bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
1094 programming_error ("both page counts are out of bounds");
1096 if (n == 1 && valid_n)
1098 bool rag = ragged () || (is_last () && ragged_last ());
1099 Real height = page_height (first_page_num, is_last ());
1101 if (1 >= min_p_count)
1102 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
1103 if (1 < cached_line_details_.size ())
1104 m_res = space_systems_on_2_pages (configuration, first_page_num);
1108 Page_spacer ps (cached_line_details_, first_page_num, this);
1110 if (n >= min_p_count || !valid_n)
1111 n_res = ps.solve (n);
1112 if (n < cached_line_details_.size () || !valid_n)
1113 m_res = ps.solve (n+1);
1116 m_res = finalize_spacing_result (configuration, m_res);
1117 n_res = finalize_spacing_result (configuration, n_res);
1119 Real page_spacing_weight = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1120 n_res.demerits_ += penalty_for_fewer_pages * page_spacing_weight;
1122 if (n_res.force_.size ())
1123 n_res.force_.back () += penalty_for_fewer_pages;
1125 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
1129 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
1131 cache_line_details (configuration);
1132 Page_spacer ps (cached_line_details_, first_page_num, this);
1134 return finalize_spacing_result (configuration, ps.solve ());
1138 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
1139 vsize first_page_num)
1141 Page_spacing_result res;
1142 Page_spacing space (page_height (first_page_num, false), this);
1145 vsize page_first_line = 0;
1147 cache_line_details (configuration);
1148 while (line < cached_line_details_.size ())
1152 space.resize (page_height (first_page_num + page, false));
1154 int system_count_on_this_page = 0;
1155 while (system_count_on_this_page < systems_per_page_
1156 && line < cached_line_details_.size ())
1158 Line_details const &cur_line = cached_line_details_[line];
1159 space.append_system (cur_line);
1160 system_count_on_this_page += cur_line.compressed_nontitle_lines_count_;
1163 if (cur_line.page_permission_ == ly_symbol2scm ("force"))
1167 res.systems_per_page_.push_back (line - page_first_line);
1169 res.force_.push_back (space.force_);
1170 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1171 if (system_count_on_this_page != systems_per_page_)
1173 res.penalty_ += TERRIBLE_SPACING_PENALTY;
1174 res.system_count_status_ |= ((system_count_on_this_page < systems_per_page_))
1175 ? SYSTEM_COUNT_TOO_FEW : SYSTEM_COUNT_TOO_MANY;
1178 page_first_line = line;
1181 /* Recalculate forces for the last page because we know now that is
1182 really the last page. */
1183 space.resize (page_height (first_page_num + page, true));
1184 res.force_.back () = space.force_;
1185 return finalize_spacing_result (configuration, res);
1189 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
1191 // TODO: add support for min/max-systems-per-page.
1192 Page_spacing_result res;
1194 vsize page_first_line = 0;
1195 Page_spacing space (page_height (first_page_num, false), this);
1197 cache_line_details (configuration);
1198 for (vsize line = 0; line < cached_line_details_.size (); line++)
1200 Real prev_force = space.force_;
1201 space.append_system (cached_line_details_[line]);
1202 if ((line > page_first_line)
1203 && (isinf (space.force_)
1205 && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
1207 res.systems_per_page_.push_back (line - page_first_line);
1208 res.force_.push_back (prev_force);
1209 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1211 space.resize (page_height (first_page_num + page, false));
1213 space.append_system (cached_line_details_[line]);
1214 page_first_line = line;
1217 if (line == cached_line_details_.size () - 1)
1219 /* This is the last line */
1220 /* When the last page height was computed, we did not know yet that it
1221 * was the last one. If the systems put on it don't fit anymore, the last
1222 * system is moved to a new page */
1223 space.resize (page_height (first_page_num + page, true));
1224 if ((line > page_first_line) && (isinf (space.force_)))
1226 res.systems_per_page_.push_back (line - page_first_line);
1227 res.force_.push_back (prev_force);
1228 /* the last page containing the last line */
1229 space.resize (page_height (first_page_num + page + 1, true));
1231 space.append_system (cached_line_details_[line]);
1232 res.systems_per_page_.push_back (1);
1233 res.force_.push_back (space.force_);
1234 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1235 res.penalty_ += cached_line_details_[line].page_penalty_;
1239 res.systems_per_page_.push_back (line + 1 - page_first_line);
1240 res.force_.push_back (space.force_);
1241 res.penalty_ += cached_line_details_[line].page_penalty_;
1245 return finalize_spacing_result (configuration, res);
1248 /* Calculate demerits and fix res.systems_per_page_ so that
1249 it refers to the original line numbers, not the ones given by compress_lines (). */
1251 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
1253 if (res.force_.empty ())
1256 cache_line_details (configuration);
1257 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
1259 Real line_force = 0;
1260 Real line_penalty = 0;
1261 Real page_demerits = res.penalty_;
1262 Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1264 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
1266 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
1267 line_penalty += uncompressed_line_details_[i].break_penalty_;
1270 for (vsize i = 0; i < res.force_.size (); i++)
1272 Real f = res.force_[i];
1274 page_demerits += min(f*f, BAD_SPACING_PENALTY);
1277 /* for a while we tried averaging page and line forces across pages instead
1278 of summing them, but it caused a problem: if there is a single page
1279 with a very bad page force (for example because of a forced page break),
1280 the page breaker will put in a _lot_ of pages so that the bad force
1281 becomes averaged out over many pages. */
1282 res.demerits_ = line_force + line_penalty + page_demerits * page_weighting;
1287 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
1288 are by far the most common cases, we have special functions for them.
1290 space_systems_on_1_page has a different calling convention than most of the
1291 space_systems functions. This is because space_systems_on_1_page is (unlike
1292 the other space_systems functions) sometimes called on subsets of a full
1295 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
1297 Page_spacing space (page_height, this);
1298 Page_spacing_result ret;
1301 for (vsize i = 0; i < lines.size (); i++)
1303 space.append_system (lines[i]);
1304 line_count += lines[i].compressed_nontitle_lines_count_;
1307 ret.systems_per_page_.push_back (lines.size ());
1308 ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
1309 ret.penalty_ = line_count_penalty (line_count) + lines.back ().page_penalty_ + lines.back ().turn_penalty_;
1310 ret.system_count_status_ |= line_count_status (line_count);
1312 /* don't do finalize_spacing_result () because we are only an internal function */
1317 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
1319 Real page1_height = page_height (first_page_num, false);
1320 Real page2_height = page_height (first_page_num + 1, is_last ());
1321 bool ragged1 = ragged ();
1322 bool ragged2 = ragged () || (is_last () && ragged_last ());
1324 /* if there is a forced break, this reduces to 2 1-page problems */
1325 cache_line_details (configuration);
1326 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
1327 if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
1329 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
1330 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
1331 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
1332 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
1334 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
1335 p1.force_.push_back (p2.force_[0]);
1336 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
1337 p1.system_count_status_ |= p2.system_count_status_;
1341 vector<Real> page1_force;
1342 vector<Real> page2_force;
1344 // page1_penalty and page2_penalty store the penalties based
1345 // on min-systems-per-page and max-systems-per-page.
1346 vector<Real> page1_penalty;
1347 vector<Real> page2_penalty;
1349 // page1_status and page2_status keep track of whether the min-systems-per-page
1350 // and max-systems-per-page constraints are satisfied.
1351 vector<int> page1_status;
1352 vector<int> page2_status;
1354 Page_spacing page1 (page1_height, this);
1355 Page_spacing page2 (page2_height, this);
1356 int page1_line_count = 0;
1357 int page2_line_count = 0;
1359 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
1360 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
1361 page1_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1362 page2_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1363 page1_status.resize (cached_line_details_.size () - 1, 0);
1364 page2_status.resize (cached_line_details_.size () - 1, 0);
1366 /* find the page 1 and page 2 forces for each page-breaking position */
1367 for (vsize i = 0; i < page1_force.size (); i++)
1369 page1.append_system (cached_line_details_[i]);
1370 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1371 page1_line_count += cached_line_details_[i].compressed_nontitle_lines_count_;
1372 page2_line_count += cached_line_details_[cached_line_details_.size () - 1 - i].compressed_nontitle_lines_count_;
1374 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1375 page1_penalty[i] = line_count_penalty (page1_line_count);
1376 page1_status[i] = line_count_status (page1_line_count);
1379 page2_force[page2_force.size () - 1 - i] =
1380 (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1382 page2_force[page2_force.size () - 1 - i] = page2.force_;
1383 page2_penalty[page2_penalty.size () - 1 - i] = line_count_penalty (page2_line_count);
1384 page2_status[page2_penalty.size () - 1 - i] = line_count_status (page2_line_count);
1387 /* find the position that minimises the sum of the page forces */
1388 vsize best_sys_count = 1;
1389 Real best_demerits = infinity_f;
1390 for (vsize i = 0; i < page1_force.size (); i++)
1392 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1394 // NOTE: we treat max-systems-per-page and min-systems-per-page as soft
1395 // constraints. That is, we penalize harshly when they don't happen
1396 // but we don't completely reject.
1398 + page1_penalty[i] + page2_penalty[i]
1399 + cached_line_details_[i+1].page_penalty_
1400 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1401 if (dem < best_demerits)
1403 best_demerits = dem;
1404 best_sys_count = i+1;
1408 Page_spacing_result ret;
1409 ret.systems_per_page_.push_back (best_sys_count);
1410 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1411 ret.force_.push_back (page1_force[best_sys_count-1]);
1412 ret.force_.push_back (page2_force[best_sys_count-1]);
1413 ret.system_count_status_ = page1_status[best_sys_count-1] | page2_status[best_sys_count-1];
1414 ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
1415 + cached_line_details_.back ().page_penalty_
1416 + cached_line_details_.back ().turn_penalty_
1417 + page1_penalty[best_sys_count-1] + page2_penalty[best_sys_count-1];
1419 /* don't do finalize_spacing_result () because we are only an internal function */
1424 Page_breaking::all_lines_stretched (vsize configuration)
1426 cache_line_details (configuration);
1427 for (vsize i = 0; i < cached_line_details_.size (); i++)
1428 if (cached_line_details_[i].force_ < 0)
1434 Page_breaking::Line_division
1435 Page_breaking::current_configuration (vsize configuration_index) const
1437 return current_configurations_[configuration_index];
1441 Page_breaking::is_last () const
1443 return current_end_breakpoint_ == last_break_position ();
1447 Page_breaking::ends_score () const
1449 return breaks_[current_end_breakpoint_].score_ender_;
1453 Page_breaking::last_break_position () const
1455 return breaks_.size () - 1;
1458 // This gives the minimum distance between the top of the
1459 // printable area (ie. the bottom of the top-margin) and
1460 // the extent box of the topmost system.
1462 Page_breaking::min_whitespace_at_top_of_page (Line_details const &line) const
1464 SCM first_system_spacing = book_->paper_->c_variable ("top-system-spacing");
1466 first_system_spacing = book_->paper_->c_variable ("top-markup-spacing");
1468 Real min_distance = -infinity_f;
1471 Page_layout_problem::read_spacing_spec (first_system_spacing,
1473 ly_symbol2scm ("minimum-distance"));
1474 Page_layout_problem::read_spacing_spec (first_system_spacing,
1476 ly_symbol2scm ("padding"));
1478 // FIXME: take into account the height of the header
1479 Real translate = max (line.shape_.begin_[UP], line.shape_.rest_[UP]);
1480 return max (0.0, max (padding, min_distance - translate));
1484 Page_breaking::min_whitespace_at_bottom_of_page (Line_details const &line) const
1486 SCM last_system_spacing = book_->paper_->c_variable ("last-bottom-spacing");
1487 Real min_distance = -infinity_f;
1490 Page_layout_problem::read_spacing_spec (last_system_spacing,
1492 ly_symbol2scm ("minimum-distance"));
1493 Page_layout_problem::read_spacing_spec (last_system_spacing,
1495 ly_symbol2scm ("padding"));
1497 // FIXME: take into account the height of the footer
1498 Real translate = min (line.shape_.begin_[DOWN], line.shape_.rest_[DOWN]);
1499 return max (0.0, max (padding, min_distance + translate));
1503 Page_breaking::orphan_penalty () const
1505 return orphan_penalty_;