2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2006--2011 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/>.
20 #include "page-spacing.hh"
22 #include "international.hh"
24 #include "page-breaking.hh"
28 Page_spacing::calc_force ()
30 Real height = page_height_
31 - breaker_->min_whitespace_at_top_of_page (first_line_)
32 - breaker_->min_whitespace_at_bottom_of_page (last_line_);
34 if (rod_height_ + last_line_.bottom_padding_ >= height)
37 force_ = (height - rod_height_ - last_line_.bottom_padding_ - spring_len_)
38 / max (0.1, inverse_spring_k_);
42 Page_spacing::resize (Real new_height)
44 page_height_ = new_height;
49 Page_spacing::append_system (const Line_details &line)
53 rod_height_ += line.tallness_;
54 spring_len_ += last_line_.spring_length (line);
59 rod_height_ += line.full_height ();
63 rod_height_ += account_for_footnotes (line);
64 inverse_spring_k_ += line.inverse_hooke_;
72 Page_spacing::account_for_footnotes (Line_details const &line)
74 Real footnote_height = 0.0;
75 Real in_note_height = 0.0;
76 bool has_in_notes = false;
77 for (vsize i = 0; i < line.in_note_heights_.size (); i++)
79 in_note_height += (has_in_notes
81 : breaker_->in_note_padding ());
83 in_note_height += line.in_note_heights_[i];
86 for (vsize i = 0; i < line.footnote_heights_.size (); i++)
88 footnote_height += (has_footnotes_
90 : (breaker_->footnote_separator_stencil_height ()
91 + breaker_->footnote_padding ()
92 + breaker_->footnote_number_raise ()));
94 has_footnotes_ = true;
95 footnote_height += line.footnote_heights_[i];
96 footnote_height += breaker_->footnote_padding ();
99 return (in_note_height
101 ? breaker_->in_note_padding ()
106 ? breaker_->footnote_padding () + breaker_->footnote_footer_padding ()
111 Page_spacing::prepend_system (const Line_details &line)
114 spring_len_ += line.spring_length (first_line_);
118 rod_height_ -= first_line_.full_height ();
119 rod_height_ += first_line_.tallness_;
120 rod_height_ += line.full_height ();
121 rod_height_ += account_for_footnotes (line);
122 inverse_spring_k_ += line.inverse_hooke_;
130 Page_spacing::clear ()
132 force_ = rod_height_ = spring_len_ = 0;
133 inverse_spring_k_ = 0;
134 has_footnotes_ = false;
137 Page_spacer::Page_spacer (vector<Line_details> const &lines, vsize first_page_num, Page_breaking const *breaker)
140 first_page_num_ = first_page_num;
143 ragged_ = breaker->ragged ();
144 ragged_last_ = breaker->is_last () && breaker->ragged_last ();
148 Page_spacer::solve ()
150 if (simple_state_.empty ())
152 simple_state_.resize (lines_.size ());
153 for (vsize i = 0; i < lines_.size (); ++i)
154 calc_subproblem (VPOS, i);
157 Page_spacing_result ret;
158 ret.penalty_ = simple_state_.back ().penalty_
159 + lines_.back ().page_penalty_ + lines_.back ().turn_penalty_;
160 ret.system_count_status_ = simple_state_.back ().system_count_status_;
162 vsize system = lines_.size () - 1;
163 while (system != VPOS)
165 Page_spacing_node const &cur = simple_state_[system];
166 vsize system_count = (cur.prev_ == VPOS) ? system + 1 : system - cur.prev_;
168 ret.force_.push_back (cur.force_);
169 ret.systems_per_page_.push_back (system_count);
170 ret.demerits_ += cur.force_ * cur.force_;
174 reverse (ret.force_);
175 reverse (ret.systems_per_page_);
180 Page_spacer::solve (vsize page_count)
182 if (page_count > max_page_count_)
185 Page_spacing_result ret;
187 vsize system = lines_.size () - 1;
188 vsize extra_systems = 0;
189 vsize extra_pages = 0;
191 if (isinf (state_.at (system, page_count - 1).demerits_))
193 programming_error ("tried to space systems on a bad number of pages");
194 /* Usually, this means that we tried to cram too many systems into
195 to few pages. To avoid crashing, we look for the largest number of
196 systems that we can fit properly onto the right number of pages.
197 All the systems that don't fit get tacked onto the last page.
200 for (i = system; isinf (state_.at (i, page_count - 1).demerits_) && i; i--)
205 extra_systems = system - i;
210 /* try chopping off pages from the end */
212 for (j = page_count; j && isinf (state_.at (system, j - 1).demerits_); j--)
217 extra_pages = page_count - j;
221 return Page_spacing_result (); /* couldn't salvage it -- probably going to crash */
225 ret.force_.resize (page_count);
226 ret.systems_per_page_.resize (page_count);
227 ret.system_count_status_ = state_.at (system, page_count - 1).system_count_status_;
228 ret.penalty_ = state_.at (system, page_count - 1).penalty_
229 + lines_.back ().page_penalty_ + lines_.back ().turn_penalty_;
232 for (vsize p = page_count; p--;)
234 assert (system != VPOS);
236 Page_spacing_node const &ps = state_.at (system, p);
237 ret.force_[p] = ps.force_;
238 ret.demerits_ += ps.force_ * ps.force_;
240 ret.systems_per_page_[p] = system + 1;
242 ret.systems_per_page_[p] = system - ps.prev_;
248 ret.systems_per_page_.back () += extra_systems;
249 ret.force_.back () = BAD_SPACING_PENALTY;
253 ret.force_.insert (ret.force_.end (), extra_pages, BAD_SPACING_PENALTY);
254 ret.systems_per_page_.insert (ret.systems_per_page_.end (), extra_pages, 0);
261 Page_spacer::resize (vsize page_count)
263 assert (page_count > 0);
265 if (max_page_count_ >= page_count)
268 state_.resize (lines_.size (), page_count, Page_spacing_node ());
269 for (vsize page = max_page_count_; page < page_count; page++)
270 for (vsize line = page; line < lines_.size (); line++)
271 if (!calc_subproblem (page, line))
274 max_page_count_ = page_count;
277 // Carries out one step in the dynamic programming algorithm for putting systems
278 // on a fixed number of pages. One call to this routine calculates the best
279 // configuration for putting lines 0 through LINE-1 on PAGE+1 pages, provided that
280 // we have previously called calc_subproblem(page-1, k) for every k < LINE.
282 // This algorithm is similar to the constrained-breaking algorithm.
284 // If page == VPOS, we act on simple_state_ instead of state_. This is useful if
285 // we don't want to constrain the number of pages that the solution has. In this
286 // case, the algorithm looks more like the page-turn-page-breaking algorithm. But
287 // the subproblems look similar for both, so we reuse this method.
289 Page_spacer::calc_subproblem (vsize page, vsize line)
291 bool last = line == lines_.size () - 1;
293 // Note: if page == VPOS then we don't actually know yet which page number we're
294 // working on, so we have to recalculate the page height in the loop. Therefore
295 // our early-exit condition from the loop depends on paper_height rather than
296 // page_height (ie. we break only if we would overfill a page without margins
297 // or headers/footers). Otherwise, the algorithm would not be optimal:
298 // if our page has a very large header then perhaps
299 // we should look ahead a few systems in order to find the best solution. A
300 // good example of this is input/regression/page-spacing-tall-headfoot.ly
301 vsize page_num = page == VPOS ? 0 : page;
302 Real paper_height = breaker_->paper_height ();
303 Page_spacing space (breaker_->page_height (page_num + first_page_num_, last),
305 Page_spacing_node &cur = page == VPOS ? simple_state_[line] : state_.at (line, page);
306 bool ragged = ragged_ || (ragged_last_ && last);
309 for (vsize page_start = line + 1; page_start > page_num && page_start--;)
311 Page_spacing_node const *prev = 0;
317 prev = &simple_state_[page_start - 1];
318 space.resize (breaker_->page_height (prev->page_ + 1, last));
321 space.resize (breaker_->page_height (first_page_num_, last));
324 prev = &state_.at (page_start - 1, page - 1);
326 space.prepend_system (lines_[page_start]);
328 bool overfull = (space.rod_height_ > paper_height
330 && (space.rod_height_ + space.spring_len_ > paper_height)));
331 // This 'if' statement is a little hard to parse. It won't consider this configuration
332 // if it is overfull unless the current configuration is the first one with this start
333 // point. We also make an exception (and consider this configuration) if the previous
334 // configuration we tried had fewer lines than min-systems-per-page.
335 if (!breaker_->too_few_lines (line_count)
340 line_count += lines_[page_start].compressed_nontitle_lines_count_;
341 if (page > 0 || page_start == 0)
343 // If the last page is ragged, set its force to zero. This way, we will leave
344 // the last page half-empty rather than trying to balance things out
345 // (which only makes sense in non-ragged situations).
346 if (line == lines_.size () - 1 && ragged && last && space.force_ > 0)
349 Real demerits = space.force_ * space.force_;
351 // Clamp the demerits at BAD_SPACING_PENALTY, even if the page
352 // is overfull. This ensures that TERRIBLE_SPACING_PENALTY takes
353 // precedence over overfull pages.
354 demerits = min (demerits, BAD_SPACING_PENALTY);
355 demerits += (prev ? prev->demerits_ : 0);
357 Real penalty = breaker_->line_count_penalty (line_count);
359 penalty += lines_[page_start - 1].page_penalty_
360 + (page % 2 == 0) ? lines_[page_start - 1].turn_penalty_ : 0;
362 /* Deal with widow/orphan lines */
363 /* Last line of paragraph is first line on the new page */
365 && (page_start < lines_.size ())
366 && (lines_[page_start].last_markup_line_))
367 penalty += breaker_->orphan_penalty ();
368 /* First line of paragraph is last line on the previous page */
370 && (page_start < lines_.size ())
371 && (lines_[page_start - 1].first_markup_line_))
372 penalty += breaker_->orphan_penalty ();
375 if (demerits < cur.demerits_ || page_start == line)
377 cur.demerits_ = demerits;
378 cur.force_ = space.force_;
379 cur.penalty_ = penalty + (prev ? prev->penalty_ : 0);
380 cur.system_count_status_ = breaker_->line_count_status (line_count)
381 | (prev ? prev->system_count_status_ : 0);
382 cur.prev_ = page_start - 1;
383 cur.page_ = prev ? prev->page_ + 1 : first_page_num_;
388 && lines_[page_start - 1].page_permission_ == ly_symbol2scm ("force"))
391 return !isinf (cur.demerits_);