+/*
+ Adds a footnote stencil to each system. This stencil may
+ itself be comprised of several footnotes.
+
+ This is a long function, but it seems better to keep it intact rather than
+ splitting it into parts.
+*/
+
+void
+Page_layout_problem::add_footnotes_to_lines (SCM lines, int counter, Paper_book *pb)
+{
+ /*
+ first, we have to see how many footnotes are on this page.
+ we need to do this first so that we can line them up
+ */
+
+ Output_def *paper = pb->paper_;
+
+ if (!paper)
+ {
+ programming_error ("Cannot get footnotes because there is no valid paper block.");
+ return;
+ }
+
+ SCM number_footnote_table = pb->top_paper ()->c_variable ("number-footnote-table");
+ if (!scm_is_pair (number_footnote_table))
+ number_footnote_table = SCM_EOL;
+ SCM numbering_function = paper->c_variable ("footnote-numbering-function");
+ SCM layout = paper->self_scm ();
+ SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
+ paper->self_scm ());
+ Real padding = robust_scm2double (paper->c_variable ("footnote-padding"), 0.0);
+ Real number_raise = robust_scm2double (paper->c_variable ("footnote-number-raise"), 0.0);
+
+ vector<Grob *> 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;
+ SCM in_text_numbers = SCM_EOL;
+ /*
+ TODO: This recalculates numbering every time this function is called, including once
+ after the balloon prints are called. Although it is not a huge computational drain,
+ it'd be more elegant to turn this calculation off when it is no longer needed.
+
+ In a separate commit, it'd be nice to streamline the way that page layout property
+ is handled so that the process of building `config's in page-breaking does result
+ in duplicated work, either by making this process less complicated or (preferably)
+ by passing its results downstream.
+ */
+
+ // 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));
+ SCM stencil = Text_interface::interpret_markup (layout, props, markup);
+ Stencil *st = Stencil::unsmob (stencil);
+ if (!st)
+ {
+ programming_error ("Your numbering function needs to return a stencil.");
+ markup = SCM_EOL;
+ stencil = Stencil (Box (Interval (0, 0), Interval (0, 0)), SCM_EOL).smobbed_copy ();
+ st = Stencil::unsmob (stencil);
+ }
+ 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++;
+ }
+
+ 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.
+ */
+
+ for (SCM p = numbers; scm_is_pair (p); p = scm_cdr (p))
+ {
+ Stencil *st = Stencil::unsmob (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))
+ {
+ // Take care of musical systems.
+ if (Grob *g = Grob::unsmob (scm_car (s)))
+ {
+ System *sys = dynamic_cast<System *> (g);
+ if (!sys)
+ {
+ programming_error ("got a grob for footnotes that wasn't a System");
+ continue;
+ }
+ Stencil mol;
+ Stencil in_note_mol;
+ extract_grob_set (sys, "footnotes-after-line-breaking", footnote_grobs);
+ for (vsize i = 0; i < footnote_grobs.size (); i++)
+ {
+ Grob *footnote = footnote_grobs[i];
+ SCM footnote_markup = footnote->get_property ("footnote-text");
+ if (Spanner *orig = dynamic_cast<Spanner *>(footnote))
+ if (orig->is_broken ())
+ footnote_markup = orig->broken_intos_[0]->get_property ("footnote-text");
+
+ SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
+ paper->self_scm ());
+
+ SCM footnote_stl = Text_interface::interpret_markup (paper->self_scm (),
+ props, footnote_markup);
+
+ Stencil footnote_stencil = *Stencil::unsmob (footnote_stl);
+ bool do_numbering = to_boolean (footnote->get_property ("automatically-numbered"));
+ if (Spanner *orig = dynamic_cast<Spanner *>(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"));
+ }
+ if (do_numbering)
+ {
+ SCM annotation_scm = scm_car (in_text_numbers);
+ footnote->set_property ("text", annotation_scm);
+ if (Spanner *orig = dynamic_cast<Spanner *>(footnote))
+ {
+ orig->set_property ("text", annotation_scm);
+ if (orig->is_broken ())
+ for (vsize i = 0; i < orig->broken_intos_.size (); i++)
+ orig->broken_intos_[i]->set_property ("text", annotation_scm);
+ }
+
+ Stencil annotation = *Stencil::unsmob (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 (to_boolean (footnote->get_property ("footnote")))
+ mol.add_at_edge (Y_AXIS, DOWN, footnote_stencil, padding);
+ else
+ in_note_mol.add_at_edge (Y_AXIS, DOWN, footnote_stencil, padding);
+ }
+ }
+ sys->set_property ("in-note-stencil", in_note_mol.smobbed_copy ());
+ sys->set_property ("footnote-stencil", mol.smobbed_copy ());
+ }
+ // Take care of top-level markups
+ else if (Prob *p = Prob::unsmob (scm_car (s)))
+ {
+ SCM stencils = p->get_property ("footnotes");
+ Stencil mol;
+
+ for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st))
+ {
+ Stencil footnote_stencil = *Stencil::unsmob (scm_caddar (st));
+ bool do_numbering = to_boolean (scm_cadar (st));
+ SCM in_text_stencil = Stencil ().smobbed_copy ();
+ if (do_numbering)
+ {
+ Stencil annotation = *Stencil::unsmob (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 (!Stencil::is_smob (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);
+ numbers = scm_cdr (numbers);
+ in_text_numbers = scm_cdr (in_text_numbers);
+ }
+ number_footnote_table = scm_cons (scm_cons (scm_caar (st),
+ in_text_stencil),
+ number_footnote_table);
+ if (!footnote_stencil.is_empty ())
+ mol.add_at_edge (Y_AXIS, DOWN, footnote_stencil, padding);
+ }
+ p->set_property ("footnote-stencil", mol.smobbed_copy ());
+ }
+ }
+
+ // note that this line of code doesn't do anything if numbering isn't turned on
+ pb->top_paper ()->set_variable (ly_symbol2scm ("number-footnote-table"), number_footnote_table);
+}
+
+Stencil