2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2006--2015 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 if (simple_state_.empty ())
161 ret.penalty_ = simple_state_.back ().penalty_
162 + lines_.back ().page_penalty_ + lines_.back ().turn_penalty_;
163 ret.system_count_status_ = simple_state_.back ().system_count_status_;
165 vsize system = lines_.size () - 1;
166 while (system != VPOS)
168 Page_spacing_node const &cur = simple_state_[system];
169 vsize system_count = (cur.prev_ == VPOS) ? system + 1 : system - cur.prev_;
171 ret.force_.push_back (cur.force_);
172 ret.systems_per_page_.push_back (system_count);
173 ret.demerits_ += cur.force_ * cur.force_;
177 reverse (ret.force_);
178 reverse (ret.systems_per_page_);
183 Page_spacer::solve (vsize page_count)
185 if (page_count > max_page_count_)
188 Page_spacing_result ret;
190 vsize system = lines_.size () - 1;
191 vsize extra_systems = 0;
192 vsize extra_pages = 0;
194 if (isinf (state_.at (system, page_count - 1).demerits_))
196 programming_error ("tried to space systems on a bad number of pages");
197 /* Usually, this means that we tried to cram too many systems into
198 to few pages. To avoid crashing, we look for the largest number of
199 systems that we can fit properly onto the right number of pages.
200 All the systems that don't fit get tacked onto the last page.
203 for (i = system; isinf (state_.at (i, page_count - 1).demerits_) && i; i--)
208 extra_systems = system - i;
213 /* try chopping off pages from the end */
215 for (j = page_count; j && isinf (state_.at (system, j - 1).demerits_); j--)
220 extra_pages = page_count - j;
224 return Page_spacing_result (); /* couldn't salvage it -- probably going to crash */
228 ret.force_.resize (page_count);
229 ret.systems_per_page_.resize (page_count);
230 ret.system_count_status_ = state_.at (system, page_count - 1).system_count_status_;
231 ret.penalty_ = state_.at (system, page_count - 1).penalty_
232 + lines_.back ().page_penalty_ + lines_.back ().turn_penalty_;
235 for (vsize p = page_count; p--;)
237 assert (system != VPOS);
239 Page_spacing_node const &ps = state_.at (system, p);
240 ret.force_[p] = ps.force_;
241 ret.demerits_ += ps.force_ * ps.force_;
243 ret.systems_per_page_[p] = system + 1;
245 ret.systems_per_page_[p] = system - ps.prev_;
251 ret.systems_per_page_.back () += extra_systems;
252 ret.force_.back () = BAD_SPACING_PENALTY;
256 ret.force_.insert (ret.force_.end (), extra_pages, BAD_SPACING_PENALTY);
257 ret.systems_per_page_.insert (ret.systems_per_page_.end (), extra_pages, 0);
264 Page_spacer::resize (vsize page_count)
266 assert (page_count > 0);
268 if (max_page_count_ >= page_count)
271 state_.resize (lines_.size (), page_count, Page_spacing_node ());
272 for (vsize page = max_page_count_; page < page_count; page++)
273 for (vsize line = page; line < lines_.size (); line++)
274 if (!calc_subproblem (page, line))
277 max_page_count_ = page_count;
280 // Carries out one step in the dynamic programming algorithm for putting systems
281 // on a fixed number of pages. One call to this routine calculates the best
282 // configuration for putting lines 0 through LINE-1 on PAGE+1 pages, provided that
283 // we have previously called calc_subproblem(page-1, k) for every k < LINE.
285 // This algorithm is similar to the constrained-breaking algorithm.
287 // If page == VPOS, we act on simple_state_ instead of state_. This is useful if
288 // we don't want to constrain the number of pages that the solution has. In this
289 // case, the algorithm looks more like the page-turn-page-breaking algorithm. But
290 // the subproblems look similar for both, so we reuse this method.
292 Page_spacer::calc_subproblem (vsize page, vsize line)
294 bool last = line == lines_.size () - 1;
296 // Note: if page == VPOS then we don't actually know yet which page number we're
297 // working on, so we have to recalculate the page height in the loop. Therefore
298 // our early-exit condition from the loop depends on paper_height rather than
299 // page_height (ie. we break only if we would overfill a page without margins
300 // or headers/footers). Otherwise, the algorithm would not be optimal:
301 // if our page has a very large header then perhaps
302 // we should look ahead a few systems in order to find the best solution. A
303 // good example of this is input/regression/page-spacing-tall-headfoot.ly
304 vsize page_num = page == VPOS ? 0 : page;
305 Real paper_height = breaker_->paper_height ();
306 Page_spacing space (breaker_->page_height (page_num + first_page_num_, last),
308 Page_spacing_node &cur = page == VPOS ? simple_state_[line] : state_.at (line, page);
309 bool ragged = ragged_ || (ragged_last_ && last);
312 for (vsize page_start = line + 1; page_start > page_num && page_start--;)
314 Page_spacing_node const *prev = 0;
320 prev = &simple_state_[page_start - 1];
321 space.resize (breaker_->page_height (prev->page_ + 1, last));
324 space.resize (breaker_->page_height (first_page_num_, last));
327 prev = &state_.at (page_start - 1, page - 1);
329 space.prepend_system (lines_[page_start]);
331 bool overfull = (space.rod_height_ > paper_height
333 && (space.rod_height_ + space.spring_len_ > paper_height)));
334 // This 'if' statement is a little hard to parse. It won't consider this configuration
335 // if it is overfull unless the current configuration is the first one with this start
336 // point. We also make an exception (and consider this configuration) if the previous
337 // configuration we tried had fewer lines than min-systems-per-page.
338 if (!breaker_->too_few_lines (line_count)
343 line_count += lines_[page_start].compressed_nontitle_lines_count_;
344 if (page > 0 || page_start == 0)
346 // If the last page is ragged, set its force to zero. This way, we will leave
347 // the last page half-empty rather than trying to balance things out
348 // (which only makes sense in non-ragged situations).
349 if (line == lines_.size () - 1 && ragged && last && space.force_ > 0)
352 Real demerits = space.force_ * space.force_;
354 // Clamp the demerits at BAD_SPACING_PENALTY, even if the page
355 // is overfull. This ensures that TERRIBLE_SPACING_PENALTY takes
356 // precedence over overfull pages.
357 demerits = min (demerits, BAD_SPACING_PENALTY);
358 demerits += (prev ? prev->demerits_ : 0);
360 Real penalty = breaker_->line_count_penalty (line_count);
362 penalty += lines_[page_start - 1].page_penalty_
363 + (page % 2 == 0) ? lines_[page_start - 1].turn_penalty_ : 0;
365 /* Deal with widow/orphan lines */
366 /* Last line of paragraph is first line on the new page */
368 && (page_start < lines_.size ())
369 && (lines_[page_start].last_markup_line_))
370 penalty += breaker_->orphan_penalty ();
371 /* First line of paragraph is last line on the previous page */
373 && (page_start < lines_.size ())
374 && (lines_[page_start - 1].first_markup_line_))
375 penalty += breaker_->orphan_penalty ();
378 if (demerits < cur.demerits_ || page_start == line)
380 cur.demerits_ = demerits;
381 cur.force_ = space.force_;
382 cur.penalty_ = penalty + (prev ? prev->penalty_ : 0);
383 cur.system_count_status_ = breaker_->line_count_status (line_count)
384 | (prev ? prev->system_count_status_ : 0);
385 cur.prev_ = page_start - 1;
386 cur.page_ = prev ? prev->page_ + 1 : first_page_num_;
391 && scm_is_eq (lines_[page_start - 1].page_permission_,
392 ly_symbol2scm ("force")))
395 return !isinf (cur.demerits_);