X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fpage-layout-problem.cc;h=3a1a84cfc5add27da5165e2f8bcffe7399040bbf;hb=ef3437c826332c1e49c257b623265bd76dbea0b1;hp=319ba396f14a5d087e6e64938f53623a2136cfd4;hpb=d8fce1e1ea2aca1a82e25e47805aef0f70f511b9;p=lilypond.git diff --git a/lily/page-layout-problem.cc b/lily/page-layout-problem.cc index 319ba396f1..3a1a84cfc5 100644 --- a/lily/page-layout-problem.cc +++ b/lily/page-layout-problem.cc @@ -1,7 +1,7 @@ /* This file is part of LilyPond, the GNU music typesetter. - Copyright (C) 2009--2011 Joe Neeman + Copyright (C) 2009--2014 Joe Neeman LilyPond is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -35,13 +35,13 @@ #include "text-interface.hh" /* - Returns the number of footntoes associated with a given line. + Returns the number of footnotes associated with a given line. */ -vsize -Page_layout_problem::get_footnote_count (SCM lines) +vector +Page_layout_problem::get_footnote_grobs (SCM lines) { - vsize fn_count = 0; + vector footnotes; for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s)) { if (Grob *g = unsmob_grob (scm_car (s))) @@ -52,7 +52,8 @@ Page_layout_problem::get_footnote_count (SCM lines) programming_error ("got a grob for footnotes that wasn't a System"); continue; } - fn_count += sys->num_footnotes (); + extract_grob_set (sys, "footnotes-after-line-breaking", footnote_grobs); + footnotes.insert (footnotes.end (), footnote_grobs.begin (), footnote_grobs.end ()); } else if (Prob *p = unsmob_prob (scm_car (s))) { @@ -60,11 +61,18 @@ Page_layout_problem::get_footnote_count (SCM lines) if (stencils == SCM_EOL) continue; for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st)) - fn_count++; + footnotes.push_back (0); } } - return fn_count; + return footnotes; +} + +vsize +Page_layout_problem::get_footnote_count (SCM lines) +{ + vector notes = get_footnote_grobs (lines); + return notes.size (); } SCM @@ -100,7 +108,7 @@ Page_layout_problem::get_footnotes_from_lines (SCM lines) programming_error ("Systems on a page must be a prob or grob."); } - return scm_reverse (out); + return scm_reverse_x (out, SCM_EOL); } /* @@ -137,7 +145,8 @@ Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book Real padding = robust_scm2double (paper->c_variable ("footnote-padding"), 0.0); Real number_raise = robust_scm2double (paper->c_variable ("footnote-number-raise"), 0.0); - vsize fn_count = get_footnote_count (lines); + vector fn_grobs = get_footnote_grobs (lines); + vsize fn_count = fn_grobs.size (); // now, make the footnote stencils with the numbering function SCM numbers = SCM_EOL; @@ -152,45 +161,53 @@ Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book in duplicated work, either by making this process less complicated or (preferably) by passing its results downstream. */ - vector footnote_number_markups; // Holds the numbering markups. - vector footnote_number_stencils; // Holds translated versions of the stencilized numbering markups. + + // find the maximum X_AXIS length + Real max_length = -infinity_f; + for (vsize i = 0; i < fn_count; i++) { + if (fn_grobs[i]) + { + SCM assertion_function = fn_grobs[i]->get_property ("numbering-assertion-function"); + if (ly_is_procedure (assertion_function)) + (void) scm_call_1 (assertion_function, scm_from_int (counter)); + } SCM markup = scm_call_1 (numbering_function, scm_from_int (counter)); - Stencil *s = unsmob_stencil (Text_interface::interpret_markup (layout, props, markup)); - if (!s) + SCM stencil = Text_interface::interpret_markup (layout, props, markup); + Stencil *st = unsmob_stencil (stencil); + if (!st) { programming_error ("Your numbering function needs to return a stencil."); markup = SCM_EOL; - s = new Stencil (Box (Interval (0, 0), Interval (0, 0)), SCM_EOL); + stencil = Stencil (Box (Interval (0, 0), Interval (0, 0)), SCM_EOL).smobbed_copy (); + st = unsmob_stencil (stencil); } - footnote_number_markups.push_back (markup); - footnote_number_stencils.push_back (s); + in_text_numbers = scm_cons (markup, in_text_numbers); + numbers = scm_cons (stencil, numbers); + + if (!st->extent (X_AXIS).is_empty ()) + max_length = max (max_length, st->extent (X_AXIS)[RIGHT]); + counter++; } - // find the maximum X_AXIS length - Real max_length = -infinity_f; - for (vsize i = 0; i < fn_count; i++) - max_length = max (max_length, footnote_number_stencils[i]->extent (X_AXIS).length ()); + in_text_numbers = scm_reverse_x (in_text_numbers, SCM_EOL); + numbers = scm_reverse_x (numbers, SCM_EOL); /* translate each stencil such that it attains the correct maximum length and bundle the footnotes into a scheme object. */ - SCM *tail = &numbers; - SCM *in_text_tail = &in_text_numbers; - for (vsize i = 0; i < fn_count; i++) + for (SCM p = numbers; scm_is_pair (p); p = scm_cdr (p)) { - *in_text_tail = scm_cons (footnote_number_markups[i], SCM_EOL); - in_text_tail = SCM_CDRLOC (*in_text_tail); - footnote_number_stencils[i]->translate_axis ((max_length - - footnote_number_stencils[i]->extent (X_AXIS).length ()), - X_AXIS); - *tail = scm_cons (footnote_number_stencils[i]->smobbed_copy (), SCM_EOL); - tail = SCM_CDRLOC (*tail); + Stencil *st = unsmob_stencil (scm_car (p)); + if (!st->extent (X_AXIS).is_empty ()) + st->translate_axis ((max_length - st->extent (X_AXIS)[RIGHT]), + X_AXIS); } + // build the footnotes for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s)) @@ -221,14 +238,14 @@ Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book SCM footnote_stl = Text_interface::interpret_markup (paper->self_scm (), props, footnote_markup); - Stencil *footnote_stencil = unsmob_stencil (footnote_stl); + Stencil footnote_stencil = *unsmob_stencil (footnote_stl); bool do_numbering = to_boolean (footnote->get_property ("automatically-numbered")); if (Spanner *orig = dynamic_cast(footnote)) { if (orig->is_broken ()) for (vsize i = 0; i < orig->broken_intos_.size (); i++) do_numbering = do_numbering - || to_boolean (orig->broken_intos_[i]->get_property ("automatically-numbered")); + || to_boolean (orig->broken_intos_[i]->get_property ("automatically-numbered")); } if (do_numbering) { @@ -242,21 +259,21 @@ Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book orig->broken_intos_[i]->set_property ("text", annotation_scm); } - Stencil *annotation = unsmob_stencil (scm_car (numbers)); - annotation->translate_axis ((footnote_stencil->extent (Y_AXIS)[UP] - + number_raise - - annotation->extent (Y_AXIS)[UP]), - Y_AXIS); - footnote_stencil->add_at_edge (X_AXIS, LEFT, *annotation, 0.0); + Stencil annotation = *unsmob_stencil (scm_car (numbers)); + annotation.translate_axis ((footnote_stencil.extent (Y_AXIS)[UP] + + number_raise + - annotation.extent (Y_AXIS)[UP]), + Y_AXIS); + footnote_stencil.add_at_edge (X_AXIS, LEFT, annotation, 0.0); numbers = scm_cdr (numbers); in_text_numbers = scm_cdr (in_text_numbers); } - if (!footnote_stencil->is_empty ()) + if (!footnote_stencil.is_empty ()) { if (to_boolean (footnote->get_property ("footnote"))) - mol.add_at_edge (Y_AXIS, DOWN, *footnote_stencil, padding); + mol.add_at_edge (Y_AXIS, DOWN, footnote_stencil, padding); else - in_note_mol.add_at_edge (Y_AXIS, DOWN, *footnote_stencil, padding); + in_note_mol.add_at_edge (Y_AXIS, DOWN, footnote_stencil, padding); } } sys->set_property ("in-note-stencil", in_note_mol.smobbed_copy ()); @@ -270,25 +287,23 @@ Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st)) { - Stencil footnote_stencil; - Stencil *footnote = unsmob_stencil (scm_caddar (st)); - footnote_stencil.add_stencil (*footnote); + Stencil footnote_stencil = *unsmob_stencil (scm_caddar (st)); bool do_numbering = to_boolean (scm_cadar (st)); SCM in_text_stencil = Stencil ().smobbed_copy (); if (do_numbering) { - Stencil *annotation = unsmob_stencil (scm_car (numbers)); + Stencil annotation = *unsmob_stencil (scm_car (numbers)); SCM in_text_annotation = scm_car (in_text_numbers); in_text_stencil = Text_interface::interpret_markup (layout, props, in_text_annotation); if (!unsmob_stencil (in_text_stencil)) in_text_stencil = SCM_EOL; - annotation->translate_axis ((footnote_stencil.extent (Y_AXIS)[UP] - + number_raise - - annotation->extent (Y_AXIS)[UP]), - Y_AXIS); - footnote_stencil.add_at_edge (X_AXIS, LEFT, *annotation, 0.0); + annotation.translate_axis ((footnote_stencil.extent (Y_AXIS)[UP] + + number_raise + - annotation.extent (Y_AXIS)[UP]), + Y_AXIS); + footnote_stencil.add_at_edge (X_AXIS, LEFT, annotation, 0.0); numbers = scm_cdr (numbers); in_text_numbers = scm_cdr (in_text_numbers); } @@ -306,7 +321,7 @@ Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book pb->top_paper ()->set_variable (ly_symbol2scm ("number-footnote-table"), number_footnote_table); } -Stencil * +Stencil Page_layout_problem::get_footnote_separator_stencil (Output_def *paper) { SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"), @@ -315,24 +330,20 @@ Page_layout_problem::get_footnote_separator_stencil (Output_def *paper) SCM markup = paper->c_variable ("footnote-separator-markup"); if (!Text_interface::is_markup (markup)) - return NULL; + return Stencil (); SCM footnote_stencil = Text_interface::interpret_markup (paper->self_scm (), props, markup); Stencil *footnote_separator = unsmob_stencil (footnote_stencil); - return footnote_separator; + return footnote_separator ? *footnote_separator : Stencil (); } -void -Page_layout_problem::add_footnotes_to_footer (SCM footnotes, Stencil *foot, Paper_book *pb) +Stencil +Page_layout_problem::add_footnotes_to_footer (SCM footnotes, Stencil foot, Paper_book *pb) { - if (!foot && scm_is_pair (footnotes)) - { - warning ("Must have a footer to add footnotes."); - return; - } + bool footnotes_found = false; Real footnote_padding = robust_scm2double (pb->paper_->c_variable ("footnote-padding"), 0.0); Real footnote_footer_padding = robust_scm2double (pb->paper_->c_variable ("footnote-footer-padding"), 0.0); @@ -348,44 +359,50 @@ Page_layout_problem::add_footnotes_to_footer (SCM footnotes, Stencil *foot, Pape if (!stencil->is_empty ()) { - foot->add_at_edge (Y_AXIS, UP, *stencil, (!footnotes_found ? footnote_footer_padding : footnote_padding)); + foot.add_at_edge (Y_AXIS, UP, *stencil, (!footnotes_found ? footnote_footer_padding : footnote_padding)); footnotes_found = true; } } if (footnotes_found) { - Stencil *separator = get_footnote_separator_stencil (pb->paper_); - if (separator) - foot->add_at_edge (Y_AXIS, UP, *separator, footnote_padding); + Stencil separator = get_footnote_separator_stencil (pb->paper_); + if (!separator.is_empty ()) + foot.add_at_edge (Y_AXIS, UP, separator, footnote_padding); } + + return foot; } Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM systems) : bottom_skyline_ (DOWN) { Prob *page = unsmob_prob (page_scm); + bottom_loose_baseline_ = 0; header_height_ = 0; footer_height_ = 0; header_padding_ = 0; footer_padding_ = 0; page_height_ = 100; + force_ = 0; if (page) { Stencil *head = unsmob_stencil (page->get_property ("head-stencil")); Stencil *foot = unsmob_stencil (page->get_property ("foot-stencil")); + Stencil foot_stencil = foot ? *foot : Stencil (); + if (pb && pb->paper_) { SCM footnotes = get_footnotes_from_lines (systems); - add_footnotes_to_footer (footnotes, foot, pb); + foot_stencil = add_footnotes_to_footer (footnotes, foot_stencil, pb); } else - warning ("A page layout problem has been initiated that cannot accommodate footnotes."); + warning (_ ("A page layout problem has been initiated that cannot accommodate footnotes.")); header_height_ = head ? head->extent (Y_AXIS).length () : 0; - footer_height_ = foot ? foot->extent (Y_AXIS).length () : 0; + footer_height_ = foot_stencil.extent (Y_AXIS).length (); page_height_ = robust_scm2double (page->get_property ("paper-height"), 100); } @@ -521,7 +538,7 @@ Page_layout_problem::set_footer_height (Real height) void Page_layout_problem::append_system (System *sys, Spring const &spring, Real indent, Real padding) { - Grob *align = sys->get_vertical_alignment (); + Grob *align = unsmob_grob (sys->get_object ("vertical-alignment")); if (!align) return; @@ -546,8 +563,8 @@ Page_layout_problem::append_system (System *sys, Spring const &spring, Real inde Skyline *sky = in_note_direction_ == UP ? &up_skyline : &down_skyline; sky->set_minimum_height (sky->max_height () + in_note_direction_ - * (in_note_padding_ - + in_note_stencil->extent (Y_AXIS).length ())); + * (in_note_padding_ + + in_note_stencil->extent (Y_AXIS).length ())); } /* @@ -558,15 +575,26 @@ Page_layout_problem::append_system (System *sys, Spring const &spring, Real inde */ Real minimum_distance = up_skyline.distance (bottom_skyline_, robust_scm2double (sys->get_property ("skyline-horizontal-padding"), - 0)) + 0)) + padding; Spring spring_copy = spring; spring_copy.ensure_min_distance (minimum_distance); springs_.push_back (spring_copy); + if (elts.size () && !is_spaceable (elts[0])) + { + // store the minimum distance, considering relative indents, + // for a loose line + Skyline first_skyline (UP); + Skyline_pair *sky = Skyline_pair::unsmob (elts[0]->get_property ("vertical-skylines")); + if (sky) + first_skyline.merge ((*sky)[UP]); + first_skyline.shift (indent); + minimum_distance = first_skyline.distance (bottom_skyline_) - bottom_loose_baseline_; + } bottom_skyline_ = down_skyline; - elements_.push_back (Element (elts, minimum_offsets, padding)); + elements_.push_back (Element (elts, minimum_offsets, minimum_distance, padding)); // Add the springs for the VerticalAxisGroups in this system. @@ -580,12 +608,17 @@ Page_layout_problem::append_system (System *sys, Spring const &spring, Real inde { if (is_spaceable (elts[i])) { - // We don't add a spring for the first staff, since - // we are only adding springs _between_ staves here. if (!found_spaceable_staff) { + // Ensure space for any loose lines above this system + if (i > 0) + springs_.back ().ensure_min_distance (bottom_loose_baseline_ + - minimum_offsets_with_min_dist[i] + + padding); found_spaceable_staff = true; last_spaceable_staff = i; + // We don't add a spring for the first staff, since + // we are only adding springs _between_ staves here. continue; } @@ -613,9 +646,12 @@ Page_layout_problem::append_system (System *sys, Spring const &spring, Real inde } } + bottom_loose_baseline_ = found_spaceable_staff + ? ( minimum_offsets_with_min_dist[last_spaceable_staff] + - minimum_offsets_with_min_dist.back ()) + : 0; + // Corner case: there was only one staff, and it wasn't spaceable. - // Mark it spaceable, because we do not allow non-spaceable staves - // to be at the top or bottom of a system. if (!found_spaceable_staff && elts.size ()) mark_as_spaceable (elts[0]); } @@ -629,7 +665,8 @@ Page_layout_problem::append_prob (Prob *prob, Spring const &spring, Real padding if (sky) { - minimum_distance = (*sky)[UP].distance (bottom_skyline_); + minimum_distance = max ((*sky)[UP].distance (bottom_skyline_), + bottom_loose_baseline_); bottom_skyline_ = (*sky)[DOWN]; } else if (Stencil *sten = unsmob_stencil (prob->get_property ("stencil"))) @@ -640,6 +677,7 @@ Page_layout_problem::append_prob (Prob *prob, Spring const &spring, Real padding bottom_skyline_.clear (); bottom_skyline_.set_minimum_height (iv[DOWN]); } + bottom_loose_baseline_ = 0.0; Spring spring_copy = spring; if (tight_spacing) @@ -655,28 +693,47 @@ Page_layout_problem::append_prob (Prob *prob, Spring const &spring, Real padding elements_.push_back (Element (prob, padding)); } +/** + For ragged-last pages, we usually want to stretch the page so that it + is not much more compressed than the previous page. Here, if ragged is + true and you pass a value of fixed_force that !isinf, then I will try + to space this page using the given force. If it does not fit, I will + resort to just filling the page (non-raggedly). +*/ void -Page_layout_problem::solve_rod_spring_problem (bool ragged) +Page_layout_problem::solve_rod_spring_problem (bool ragged, Real fixed_force) { Simple_spacer spacer; for (vsize i = 0; i < springs_.size (); ++i) spacer.add_spring (springs_[i]); - spacer.solve (page_height_, ragged); + if (ragged && !isinf (fixed_force)) + { + // We need to tell the spacer it isn't ragged. Otherwise, it will + // refuse to stretch. + spacer.solve (page_height_, false); + + if (spacer.configuration_length (fixed_force) <= page_height_) + spacer.set_force (fixed_force); + } + else + spacer.solve (page_height_, ragged); + solution_ = spacer.spring_positions (); + force_ = spacer.force (); if (!spacer.fits ()) { Real overflow = spacer.configuration_length (spacer.force ()) - page_height_; if (ragged && overflow < 1e-6) - warning (_ ("cannot fit music on page: ragged-spacing was requested, but page was compressed")); + warning (_ ("ragged-bottom was specified, but page must be compressed")); else { - warning (_f ("cannot fit music on page: overflow is %f", + warning (_f ("compressing over-full page by %.1f staff-spaces", overflow)); - warning (_ ("compressing music to fit")); + force_ = -infinity_f; vsize space_count = solution_.size (); Real spacing_increment = overflow / (space_count - 2); for (vsize i = 2; i < space_count; i++) @@ -685,6 +742,12 @@ Page_layout_problem::solve_rod_spring_problem (bool ragged) } } +Real +Page_layout_problem::force () const +{ + return force_; +} + // The solution_ vector stores the position of every live VerticalAxisGroup // and every title. From that information, // 1) within each system, stretch the staves so they land at the right position @@ -769,7 +832,12 @@ Page_layout_problem::find_system_offsets () if (staff_idx) loose_line_min_distances.push_back (min_offsets[staff_idx - 1] - min_offsets[staff_idx]); else - loose_line_min_distances.push_back (elements_[i].padding - min_offsets[staff_idx]); + { + // A null line to break any staff-affinity from the previous system + loose_line_min_distances.push_back (0.0); + loose_lines.push_back (0); + loose_line_min_distances.push_back (elements_[i].padding - min_offsets[0]); + } loose_lines.push_back (staff); distribute_loose_lines (loose_lines, loose_line_min_distances, @@ -782,8 +850,11 @@ Page_layout_problem::find_system_offsets () found_spaceable_staff = true; spring_idx++; } - else + else // ! is_spaceable { + if (staff->extent (staff, Y_AXIS).is_empty ()) + continue; + if (loose_lines.empty ()) loose_lines.push_back (last_spaceable_line); @@ -798,15 +869,17 @@ Page_layout_problem::find_system_offsets () // this is the first line in a system Real min_dist = 0; if (loose_lines.back ()) - // distance to the final line in the preceding system, - // including 'system-system-spacing 'padding - min_dist = (Axis_group_interface::minimum_distance (loose_lines.back (), - staff, - Y_AXIS) - + elements_[i].padding); + { + // distance to the final line in the preceding system, + // including 'system-system-spacing 'padding + min_dist = elements_[i].min_distance + elements_[i].padding; + // A null line to break any staff-affinity for the previous system + loose_line_min_distances.push_back (0.0); + loose_lines.push_back (0); + } else if (!last_title_extent.is_empty ()) // distance to the preceding title, - // including 'markup-system-spacing 'padding + // including 'markup-system-wg 'padding min_dist = (staff->extent (staff, Y_AXIS)[UP] - last_title_extent[DOWN] + elements_[i].padding); else // distance to the top margin @@ -869,16 +942,24 @@ Page_layout_problem::distribute_loose_lines (vector const &loose_lines, vector solution = spacer.spring_positions (); for (vsize i = 1; i + 1 < solution.size (); ++i) - { - Real system_offset = scm_to_double (loose_lines[i]->get_property ("system-Y-offset")); - loose_lines[i]->translate_axis (first_translation - solution[i] - system_offset, Y_AXIS); - } + if (loose_lines[i]) + { + Real system_offset = scm_to_double (loose_lines[i]->get_property ("system-Y-offset")); + loose_lines[i]->translate_axis (first_translation - solution[i] - system_offset, Y_AXIS); + } +} + +SCM +Page_layout_problem::fixed_force_solution (Real force) +{ + solve_rod_spring_problem (true, force); + return find_system_offsets (); } SCM Page_layout_problem::solution (bool ragged) { - solve_rod_spring_problem (ragged); + solve_rod_spring_problem (ragged, -infinity_f); return find_system_offsets (); }