From 233aad0ba9781e43424c4e77a859e42b660210e6 Mon Sep 17 00:00:00 2001 From: Mike Solomon Date: Thu, 28 Jul 2011 16:47:08 +0200 Subject: [PATCH] Adds automatic numbering to footnotes. There are currently two types of automatic numbering possible: automatic numbering where the footnote resets on each page and automatic numbering where the footnotes increase in sequence over pages. The bulk of the commit lies in paper-layout-problem.cc. The two new regtests show how this is done. --- .../footnote-auto-numbering-page-reset.ly | 40 ++++ input/regression/footnote-auto-numbering.ly | 40 ++++ input/regression/footnote-break-visibility.ly | 4 + input/regression/footnote-spanner.ly | 5 +- input/regression/footnote.ly | 4 + lily/align-interface.cc | 2 +- lily/balloon.cc | 7 +- lily/constrained-breaking.cc | 3 +- lily/include/page-breaking.hh | 6 +- lily/include/page-layout-problem.hh | 5 +- lily/include/system.hh | 3 +- lily/output-def-scheme.cc | 2 +- lily/page-breaking.cc | 51 +++-- lily/page-layout-problem.cc | 210 ++++++++++++++++-- lily/page-spacing.cc | 3 +- lily/paper-book.cc | 2 +- lily/paper-system.cc | 15 +- lily/system.cc | 31 +-- ly/music-functions-init.ly | 33 ++- ly/paper-defaults-init.ly | 6 +- scm/define-grobs.scm | 2 + scm/define-markup-commands.scm | 79 ++++++- scm/lily-library.scm | 17 ++ scm/output-lib.scm | 19 ++ 24 files changed, 500 insertions(+), 89 deletions(-) create mode 100644 input/regression/footnote-auto-numbering-page-reset.ly create mode 100644 input/regression/footnote-auto-numbering.ly diff --git a/input/regression/footnote-auto-numbering-page-reset.ly b/input/regression/footnote-auto-numbering-page-reset.ly new file mode 100644 index 0000000000..c5436e82ca --- /dev/null +++ b/input/regression/footnote-auto-numbering-page-reset.ly @@ -0,0 +1,40 @@ +\version "2.15.0" +\header { + texidoc = "Lilypond does footnotes." +} + +\paper { + footnote-numbering-function = #symbol-footnotes +} + +#(set-default-paper-size "a6") +\book { + +\markup { + a \footnote b c + \footnote d e + \footnote f g +} + +\markup { h i } + +\relative c' { +\autoFootnoteGrob #'NoteHead #'(1 . -1) \markup { j } +a b c d } + +\pageBreak + +\markup { k \footnote l m } + +\relative c' { a1 } + +\relative c' { + d4 e + < f a-\autoFootnote #'(1 . -1) \markup { n } c > + \autoFootnoteGrob #'Beam #'(1 . 1) \markup { o } + \autoFootnoteGrob #'Hairpin #'(1 . 1) \markup { p } + a8\< [ b c d ] a4 b c | + d a b c | + d a b c | + d a b c\f | +}} diff --git a/input/regression/footnote-auto-numbering.ly b/input/regression/footnote-auto-numbering.ly new file mode 100644 index 0000000000..46d0448a8a --- /dev/null +++ b/input/regression/footnote-auto-numbering.ly @@ -0,0 +1,40 @@ +\version "2.15.0" +\header { + texidoc = "Lilypond does footnotes." +} + +\paper { + reset-footnotes-on-new-page = ##f +} + +#(set-default-paper-size "a6") +\book { + +\markup { + a \footnote b c + \footnote d e + \footnote f g +} + +\markup { h i } + +\relative c' { +\autoFootnoteGrob #'NoteHead #'(1 . -1) \markup { j } +a b c d } + +\pageBreak + +\markup { k \footnote l m } + +\relative c' { a1 } + +\relative c' { + d4 e + < f a-\autoFootnote #'(1 . -1) \markup { n } c > + \autoFootnoteGrob #'Beam #'(1 . 1) \markup { o } + \autoFootnoteGrob #'Hairpin #'(1 . 1) \markup { p } + a8\< [ b c d ] a4 b c | + d a b c | + d a b c | + d a b c\f | +}} diff --git a/input/regression/footnote-break-visibility.ly b/input/regression/footnote-break-visibility.ly index 23e9035a71..6159b5dc5f 100644 --- a/input/regression/footnote-break-visibility.ly +++ b/input/regression/footnote-break-visibility.ly @@ -7,6 +7,10 @@ can be overrided." #(set-default-paper-size "a6") +\paper { + footnote-auto-numbering = ##f +} + \book { \new Staff \with { \consists "Footnote_engraver" } diff --git a/input/regression/footnote-spanner.ly b/input/regression/footnote-spanner.ly index 6efca6671b..8129d6fd4b 100644 --- a/input/regression/footnote-spanner.ly +++ b/input/regression/footnote-spanner.ly @@ -6,7 +6,10 @@ annotation goes to the correct page." #(set-default-paper-size "a6") -\paper { ragged-last-bottom = ##f } +\paper { + ragged-last-bottom = ##f + footnote-auto-numbering = ##f +} \book { diff --git a/input/regression/footnote.ly b/input/regression/footnote.ly index f13347d31a..a2b1c65803 100644 --- a/input/regression/footnote.ly +++ b/input/regression/footnote.ly @@ -3,6 +3,10 @@ texidoc = "Lilypond does footnotes." } +\paper { + footnote-auto-numbering = ##f +} + #(set-default-paper-size "a6") \book { diff --git a/lily/align-interface.cc b/lily/align-interface.cc index 403f903e49..cf957449ec 100644 --- a/lily/align-interface.cc +++ b/lily/align-interface.cc @@ -291,7 +291,7 @@ Align_interface::align_elements_to_ideal_distances (Grob *me) System *sys = me->get_system (); if (sys) { - Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ())); + Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()), 0); layout.solution (true); } else diff --git a/lily/balloon.cc b/lily/balloon.cc index c4efb46b68..b9872b2cd5 100644 --- a/lily/balloon.cc +++ b/lily/balloon.cc @@ -101,13 +101,12 @@ Balloon_interface::internal_balloon_print (Grob *me, Grob *p, Offset off) SCM bt = me->get_property ("text"); SCM chain = Font_interface::text_font_alist_chain (me); - SCM stencil = Text_interface::interpret_markup (me->layout ()->self_scm (), - chain, bt); - + chain, bt); Stencil *text_stil = unsmob_stencil (stencil); - + Offset z1; + for (int i = X_AXIS; i < NO_AXES; i++) { Axis a ((Axis)i); diff --git a/lily/constrained-breaking.cc b/lily/constrained-breaking.cc index d687f1e87d..233011587f 100644 --- a/lily/constrained-breaking.cc +++ b/lily/constrained-breaking.cc @@ -552,9 +552,10 @@ Line_details::Line_details (Prob *pb, Output_def *paper) Page_layout_problem::read_spacing_spec (title_spec, &title_min_distance_, ly_symbol2scm ("minimum-distance")); SCM footnotes = pb->get_property ("footnotes"); + if (scm_is_pair (footnotes)) for (SCM s = footnotes; scm_is_pair (s); s = scm_cdr (s)) - footnotes_.push_back (unsmob_stencil (scm_car (s))); + footnotes_.push_back (unsmob_stencil (scm_cadar (s))); last_column_ = 0; force_ = 0; diff --git a/lily/include/page-breaking.hh b/lily/include/page-breaking.hh index 17f64d754b..acf6bac51c 100644 --- a/lily/include/page-breaking.hh +++ b/lily/include/page-breaking.hh @@ -126,6 +126,7 @@ public: vsize system_count () const; Real footnote_separator_stencil_height () const; Real footnote_padding () const; + Real footnote_number_raise () const; Real footnote_footer_padding () const; Real line_count_penalty (int line_count) const; int line_count_status (int line_count) const; @@ -190,6 +191,7 @@ private: vsize system_count_; Real footnote_separator_stencil_height_; Real footnote_padding_; + Real footnote_number_raise_; Real footnote_footer_padding_; int orphan_penalty_; @@ -229,7 +231,7 @@ private: void create_system_list (); void find_chunks_and_breaks (Break_predicate, Prob_break_predicate); SCM make_page (int page_num, bool last) const; - SCM get_page_configuration (SCM systems, int page_num, bool ragged, bool last); - SCM draw_page (SCM systems, SCM config, int page_num, bool last); + SCM get_page_configuration (SCM systems, int page_num, int footnote_count, bool ragged, bool last); + SCM draw_page (SCM systems, SCM config, int page_num, int footnote_num, bool last); }; #endif /* PAGE_BREAKING_HH */ diff --git a/lily/include/page-layout-problem.hh b/lily/include/page-layout-problem.hh index ff6fcece22..629528bb53 100644 --- a/lily/include/page-layout-problem.hh +++ b/lily/include/page-layout-problem.hh @@ -26,7 +26,7 @@ class Page_layout_problem { public: - Page_layout_problem (Paper_book*, SCM page, SCM systems); + Page_layout_problem (Paper_book*, SCM page, SCM systems, int footnote_count); SCM solution (bool ragged); void set_header_height (Real); @@ -34,7 +34,8 @@ public: static bool read_spacing_spec (SCM spec, Real* dest, SCM sym); static bool is_spaceable (Grob *g); static SCM get_details (Grob *g); - static SCM get_footnotes_from_lines (SCM lines, Real padding); + static vsize get_footnote_count (SCM lines); + static SCM get_footnotes_from_lines (SCM lines, int counter, Paper_book *pb); static Stencil* get_footnote_separator_stencil (Output_def *paper); static SCM get_spacing_spec (Grob *before, Grob *after, bool pure, int start, int end); static Real get_fixed_spacing (Grob *before, Grob *after, int spaceable_index, bool pure, int start, int end); diff --git a/lily/include/system.hh b/lily/include/system.hh index 940f6a5fb8..4b56238f36 100644 --- a/lily/include/system.hh +++ b/lily/include/system.hh @@ -48,7 +48,8 @@ public: int get_rank () const; vector get_footnotes_in_range (vsize st, vsize end); void get_footnote_grobs_in_range (vector &out, vsize st, vsize end); - Stencil make_footnote_stencil (Real padding); + vector *footnote_grobs (); + vsize num_footnotes (); void do_break_substitution_and_fixup_refpoints (); void post_processing (); void populate_footnote_grob_vector (); diff --git a/lily/output-def-scheme.cc b/lily/output-def-scheme.cc index d1ee310837..455702b0aa 100644 --- a/lily/output-def-scheme.cc +++ b/lily/output-def-scheme.cc @@ -196,4 +196,4 @@ LY_DEFINE (ly_paper_fonts, "ly:paper-fonts", } return font_list; -} +} \ No newline at end of file diff --git a/lily/page-breaking.cc b/lily/page-breaking.cc index fe1d163658..5d55c7a9f4 100644 --- a/lily/page-breaking.cc +++ b/lily/page-breaking.cc @@ -265,6 +265,10 @@ Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break, Prob_bre footnote_padding_ = robust_scm2double (pb->paper_->c_variable ("footnote-padding"), 0.0); footnote_footer_padding_ = robust_scm2double (pb->paper_->c_variable ("footnote-footer-padding"), 0.0); + footnote_number_raise_ = (to_boolean (pb->paper_->c_variable ("footnote-auto-numbering")) + ? robust_scm2double (pb->paper_->c_variable ("footnote-number-raise"), 0.0) + : 0.0); + if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_)) { warning (_f ("ignoring min-systems-per-page and max-systems-per-page because systems-per-page was set")); @@ -342,6 +346,12 @@ Page_breaking::footnote_footer_padding () const return footnote_footer_padding_; } +Real +Page_breaking::footnote_number_raise () const +{ + return footnote_number_raise_; +} + bool Page_breaking::too_many_lines (int line_count) const { @@ -521,15 +531,15 @@ Page_breaking::breakpoint_property (vsize breakpoint, char const *str) } SCM -Page_breaking::get_page_configuration (SCM systems, int page_num, bool ragged, bool last) +Page_breaking::get_page_configuration (SCM systems, int page_num, int footnote_count, bool ragged, bool last) { SCM dummy_page = make_page (page_num, last); - Page_layout_problem layout (book_, dummy_page, systems); + Page_layout_problem layout (book_, dummy_page, systems, footnote_count); return scm_is_pair (systems) ? layout.solution (ragged) : SCM_EOL; } SCM -Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool last) +Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, int footnote_num, bool last) { // Create a stencil for each system. SCM paper_systems = SCM_EOL; @@ -556,8 +566,16 @@ Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool las p->set_property ("configuration", configuration); Stencil *foot = unsmob_stencil (p->get_property ("foot-stencil")); - SCM footnotes = Page_layout_problem::get_footnotes_from_lines (systems, footnote_padding ()); - Page_layout_problem::add_footnotes_to_footer (footnotes, foot, unsmob_paper_book (p->get_property ("paper-book"))); + + footnote_num = (to_boolean (book_->paper_->c_variable ("reset-footnotes-on-new-page")) + ? 0 + : footnote_num); + + SCM footnotes = Page_layout_problem::get_footnotes_from_lines (systems, + footnote_num, + book_); + + Page_layout_problem::add_footnotes_to_footer (footnotes, foot, book_); p->set_property ("foot-stencil", foot->smobbed_copy ()); scm_apply_1 (page_stencil, page, SCM_EOL); @@ -584,8 +602,9 @@ Page_breaking::make_pages (vector lines_per_page, SCM systems) // themselves. If this happens before the neighbouring staves have // been laid out, bad side-effects could happen (in particular, // Align_interface::align_to_ideal_distances might be called). - SCM systems_and_configs = SCM_EOL; - + SCM systems_configs_fncounts = SCM_EOL; + vsize footnote_count = 0; + for (vsize i = 0; i < lines_per_page.size (); i++) { int page_num = i + first_page_number; @@ -593,22 +612,24 @@ Page_breaking::make_pages (vector lines_per_page, SCM systems) bool rag = ragged () || (bookpart_last_page && ragged_last ()); SCM line_count = scm_from_int (lines_per_page[i]); SCM lines = scm_list_head (systems, line_count); - SCM config = get_page_configuration (lines, page_num, rag, bookpart_last_page); - - systems_and_configs = scm_cons (scm_cons (lines, config), systems_and_configs); + int fn_lines = Page_layout_problem::get_footnote_count (lines); + SCM config = get_page_configuration (lines, page_num, footnote_count, rag, bookpart_last_page); + + systems_configs_fncounts = scm_cons (scm_list_3 (lines, config, scm_from_int ((int)footnote_count)), systems_configs_fncounts); + footnote_count += fn_lines; systems = scm_list_tail (systems, line_count); } // Now it's safe to make the pages. int page_num = first_page_number + lines_per_page.size () - 1; - for (SCM s = systems_and_configs; scm_is_pair (s); s = scm_cdr (s)) + for (SCM s = systems_configs_fncounts; scm_is_pair (s); s = scm_cdr (s)) { SCM lines = scm_caar (s); - SCM config = scm_cdar (s); - - bool bookpart_last_page = (s == systems_and_configs); - SCM page = draw_page (lines, config, page_num, bookpart_last_page); + SCM config = scm_cadar (s); + int footnote_num = scm_to_int (scm_caddar (s)); + bool bookpart_last_page = (s == systems_configs_fncounts); + SCM page = draw_page (lines, config, page_num, footnote_num, bookpart_last_page); /* collect labels */ SCM page_num_scm = scm_from_int (page_num); for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l)) diff --git a/lily/page-layout-problem.cc b/lily/page-layout-problem.cc index 07c99586ee..7445d21f62 100644 --- a/lily/page-layout-problem.cc +++ b/lily/page-layout-problem.cc @@ -34,18 +34,137 @@ #include "system.hh" #include "text-interface.hh" +/* + Returns the number of footntoes associated with a given line. +*/ + +vsize +Page_layout_problem::get_footnote_count (SCM lines) +{ + vsize fn_count = 0; + for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s)) + { + if (Grob *g = unsmob_grob (scm_car (s))) + { + System *sys = dynamic_cast (g); + if (!sys) + { + programming_error ("got a grob for footnotes that wasn't a System"); + continue; + } + fn_count += sys->num_footnotes (); + } + else if (Prob *p = unsmob_prob (scm_car (s))) + { + SCM stencils = p->get_property ("footnotes"); + if (stencils == SCM_EOL) + continue; + for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st)) + fn_count++; + } + } + + return fn_count; +} + /* Returns a stencil for the footnote of 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. */ SCM -Page_layout_problem::get_footnotes_from_lines (SCM lines, Real padding) +Page_layout_problem::get_footnotes_from_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_EOL; + } + + 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); + + vsize fn_count = get_footnote_count (lines); + + // now, make the footnote stencils with the numbering function + SCM numbers = SCM_EOL; + SCM in_text_numbers = SCM_EOL; + bool do_numbering = to_boolean (paper->c_variable ("footnote-auto-numbering")); + // if there's no numbering, skip all this + /* + 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. + */ + if (do_numbering) + { + vector footnote_number_markups; // Holds the numbering markups. + vector footnote_number_stencils; // Holds translated versions of the stencilized numbering markups. + for (vsize i = 0; i < fn_count; i++) + { + SCM markup = scm_call_1 (numbering_function, scm_from_int (counter)); + Stencil *s = unsmob_stencil (Text_interface::interpret_markup (layout, props, markup)); + if (!s) + { + 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); + } + footnote_number_markups.push_back (markup); + footnote_number_stencils.push_back (s); + counter++; + } + + // find the maximum X_AXIS length + Real max_length; + for (vsize i = 0; i < fn_count; i++) + max_length = max (max_length, footnote_number_stencils[i]->extent (X_AXIS).length ()); + + /* + 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++) + { + *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); + } + } + // build the footnotes + SCM footnotes = SCM_EOL; - // ugh...code dup from the Page_layout_problem constructor + for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s)) { + // Take care of musical systems. if (Grob *g = unsmob_grob (scm_car (s))) { System *sys = dynamic_cast (g); @@ -54,8 +173,49 @@ Page_layout_problem::get_footnotes_from_lines (SCM lines, Real padding) programming_error ("got a grob for footnotes that wasn't a System"); continue; } - footnotes = scm_cons (sys->make_footnote_stencil (padding).smobbed_copy (), footnotes); + Stencil mol; + + for (vsize i = 0; i < sys->footnote_grobs ()->size (); i++) + { + Grob *footnote = sys->footnote_grobs ()->at (i); + SCM footnote_markup = footnote->get_property ("footnote-text"); + if (Spanner *orig = dynamic_cast(footnote)) + if (orig->is_broken ()) + footnote_markup = orig->broken_intos_[0]->get_property ("footnote-text"); + + if (!Text_interface::is_markup (footnote_markup)) + continue; + + 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 = unsmob_stencil (footnote_stl); + if (do_numbering) + { + SCM annotation_scm = scm_car (in_text_numbers); + footnote->set_property ("text", annotation_scm); + if (Spanner *orig = dynamic_cast(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 = 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); + } + mol.add_at_edge (Y_AXIS, DOWN, *footnote_stencil, padding); + } + footnotes = scm_cons (mol.smobbed_copy (), footnotes); } + // Take care of top-level markups else if (Prob *p = unsmob_prob (scm_car (s))) { SCM stencils = p->get_property ("footnotes"); @@ -64,11 +224,31 @@ Page_layout_problem::get_footnotes_from_lines (SCM lines, Real padding) Stencil footnote_stencil; for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st)) - footnote_stencil.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (scm_car (st)), padding); + { + Stencil mol; + Stencil *footnote = unsmob_stencil (scm_cadar (st)); + mol.add_stencil (*footnote); + if (do_numbering) + { + Stencil *annotation = unsmob_stencil (scm_car (numbers)); + SCM in_text_annotation = scm_car (in_text_numbers); + SCM in_text_stencil = Text_interface::interpret_markup (layout, props, in_text_annotation); + if (!unsmob_stencil (in_text_stencil)) + in_text_stencil = SCM_EOL; + number_footnote_table = scm_cons (scm_cons (scm_caar (st), in_text_stencil), number_footnote_table); + annotation->translate_axis (mol.extent (Y_AXIS)[UP] + number_raise - annotation->extent(Y_AXIS)[UP], Y_AXIS); + mol.add_at_edge (X_AXIS, LEFT, *annotation, 0.0); + numbers = scm_cdr (numbers); + in_text_numbers = scm_cdr (in_text_numbers); + } + footnote_stencil.add_at_edge (Y_AXIS, DOWN, mol, padding); + } footnotes = scm_cons (footnote_stencil.smobbed_copy (), footnotes); } } + // 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); if (!scm_is_pair (footnotes)) return SCM_EOL; @@ -116,7 +296,7 @@ Page_layout_problem::add_footnotes_to_footer (SCM footnotes, Stencil *foot, Pape footnotes_found = true; } } - + if (footnotes_found) { Stencil *separator = get_footnote_separator_stencil (pb->paper_); @@ -125,7 +305,7 @@ Page_layout_problem::add_footnotes_to_footer (SCM footnotes, Stencil *foot, Pape } } -Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM systems) +Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM systems, int footnote_count) : bottom_skyline_ (DOWN) { Prob *page = unsmob_prob (page_scm); @@ -139,13 +319,17 @@ Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM syst { Stencil *head = unsmob_stencil (page->get_property ("head-stencil")); Stencil *foot = unsmob_stencil (page->get_property ("foot-stencil")); - - Real footnote_padding = 0.0; + if (pb && pb->paper_) - footnote_padding = robust_scm2double (pb->paper_->c_variable ("footnote-padding"), 0.0); - SCM footnotes = get_footnotes_from_lines (systems, footnote_padding); - add_footnotes_to_footer (footnotes, foot, pb); - + { + if (to_boolean (pb->paper_->c_variable ("reset-footnotes-on-new-page"))) + footnote_count = 0; + SCM footnotes = get_footnotes_from_lines (systems, footnote_count, pb); + add_footnotes_to_footer (footnotes, foot, pb); + } + else + 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; page_height_ = robust_scm2double (page->get_property ("paper-height"), 100); @@ -797,7 +981,7 @@ Page_layout_problem::get_fixed_spacing (Grob *before, Grob *after, int spaceable // Cache the result. As above, we ignore "end." if (pure) after_sp->cache_pure_property (cache_symbol, start, 0, scm_from_double (ret)); - + return ret; } diff --git a/lily/page-spacing.cc b/lily/page-spacing.cc index b04d3f394a..bae7ae6fb7 100644 --- a/lily/page-spacing.cc +++ b/lily/page-spacing.cc @@ -77,7 +77,8 @@ Page_spacing::account_for_footnotes (Line_details const &line) footnote_height += (has_footnotes_ ? 0.0 : (breaker_->footnote_separator_stencil_height () - + breaker_->footnote_padding ())); + + breaker_->footnote_padding () + + breaker_->footnote_number_raise ())); has_footnotes_ = true; Interval extent = line.footnotes_[i]->extent (Y_AXIS); diff --git a/lily/paper-book.cc b/lily/paper-book.cc index 9907e56d95..7fcea66c62 100644 --- a/lily/paper-book.cc +++ b/lily/paper-book.cc @@ -531,7 +531,7 @@ Paper_book::get_system_specs () ps->set_property ("first-markup-line", SCM_BOOL_F); paper_system_set_stencil (ps, *unsmob_stencil (t)); - + SCM footnotes = get_footnotes (unsmob_stencil (t)->expr ()); ps->set_property ("footnotes", footnotes); ps->set_property ("is-title", SCM_BOOL_T); diff --git a/lily/paper-system.cc b/lily/paper-system.cc index 711d919446..437a0eb450 100644 --- a/lily/paper-system.cc +++ b/lily/paper-system.cc @@ -18,6 +18,7 @@ */ #include "paper-system.hh" +#include "international.hh" #include "item.hh" Prob * @@ -59,27 +60,19 @@ get_footnotes (SCM expr) for (SCM x = scm_cdr (expr); scm_is_pair (x); x = scm_cdr (x)) { SCM footnote = get_footnotes (scm_car (x)); - if (scm_is_pair (footnote)) - { - for (SCM y = footnote; scm_is_pair (y); y = scm_cdr (y)) - { - *tail = scm_cons (scm_car (y), SCM_EOL); - tail = SCM_CDRLOC (*tail); - } - } - else if (SCM_EOL != footnote) + if (SCM_EOL != footnote) { *tail = scm_cons (footnote, SCM_EOL); tail = SCM_CDRLOC (*tail); } } - return out; + return scm_append (out); } if (head == ly_symbol2scm ("translate-stencil")) return get_footnotes (scm_caddr (expr)); if (head == ly_symbol2scm ("footnote")) - return scm_cadr (expr); + return scm_list_1 (scm_cdr (expr)); return SCM_EOL; } diff --git a/lily/system.cc b/lily/system.cc index 1cd582de75..d151911ea5 100644 --- a/lily/system.cc +++ b/lily/system.cc @@ -311,31 +311,16 @@ System::get_footnotes_in_range (vsize start, vsize end) return out; } -Stencil -System::make_footnote_stencil (Real padding) +vsize +System::num_footnotes () { - Stencil mol; - - for (vsize i = 0; i < footnote_grobs_.size (); i++) - { - SCM footnote_markup = footnote_grobs_[i]->get_property ("footnote-text"); - if (Spanner *orig = dynamic_cast(footnote_grobs_[i])) - if (orig->is_broken ()) - footnote_markup = orig->broken_intos_[0]->get_property ("footnote-text"); - - if (!Text_interface::is_markup (footnote_markup)) - continue; - - SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"), - pscore_->layout ()->self_scm ()); - - SCM footnote_stl = Text_interface::interpret_markup (pscore_->layout ()->self_scm (), - props, footnote_markup); - - mol.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (footnote_stl), padding); - } + return footnote_grobs_.size (); +} - return mol; +vector* +System::footnote_grobs () +{ + return &footnote_grobs_; } void diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly index 8fb4010f4e..3535a0a6a7 100644 --- a/ly/music-functions-init.ly +++ b/ly/music-functions-init.ly @@ -344,7 +344,7 @@ footnoteGrob = #(define-music-function (parser location grob-name offset text footnote) (symbol? number-pair? markup? markup?) (_i "Attach @var{text} to @var{grob-name} at offset @var{offset}, - with @var{text} referring to @var{footnote} (use like @code{\\once})") +with @var{text} referring to @var{footnote} (use like @code{\\once})") (make-music 'FootnoteEvent 'symbol grob-name 'X-offset (car offset) @@ -352,17 +352,46 @@ footnoteGrob = 'text text 'footnote-text footnote)) +autoFootnoteGrob = +#(define-music-function (parser location grob-name offset footnote) + (symbol? number-pair? markup?) + (_i "Footnote @var{grob-name} with the text in @var{footnote} +allowing for the footnote to be automatically numbered such that +the number appears at @var{offset}. Note that, for this to take effect, +auto-numbering must be turned on in the paper block. Otherwise, no +number will appear. Use like @code{\\once})") + #{ + \footnoteGrob $grob-name $offset \markup { "" } $footnote + #}) + footnote = #(define-music-function (parser location offset text footnote) (number-pair? markup? markup?) (_i "Attach @var{text} at @var{offset} with @var{text} referring - to @var{footnote} (use like @code{\\tweak})") +to @var{footnote} (use like @code{\\tweak})") (make-music 'FootnoteEvent 'X-offset (car offset) 'Y-offset (cdr offset) 'text text 'footnote-text footnote)) +% this function can't be a simple copy and past of the above because +% it needs to be encapsulated in a Sequential Music. +% so, there's a code dup of above :-( +autoFootnote = +#(define-music-function (parser location offset footnote) + (number-pair? markup?) + (_i "Footnote the item after which this comes with the text in +@var{footnote} allowing for the footnote to be automatically numbered +such that the number appears at @var{offset}. Note that, for this to +take effect, auto-numbering must be turned on in the paper block. +Otherwise, no number will appear. Use like @code{\\tweak})") + (make-music 'FootnoteEvent + 'X-offset (car offset) + 'Y-offset (cdr offset) + 'text (markup "") + 'footnote-text footnote)) + grace = #(def-grace-function startGraceMusic stopGraceMusic (_i "Insert @var{music} as grace notes.")) diff --git a/ly/paper-defaults-init.ly b/ly/paper-defaults-init.ly index 145fe9a52e..c68652d8a4 100644 --- a/ly/paper-defaults-init.ly +++ b/ly/paper-defaults-init.ly @@ -106,9 +106,13 @@ %% %% Footnotes %% - footnote-separator-markup = \markup { \fill-line \override #`(span-factor . 1/2) { \draw-hline } } + footnote-separator-markup = \markup \fill-line { \override #'(span-factor . 1/2) \draw-hline } footnote-padding = 0.5\mm footnote-footer-padding = 0.5\mm + footnote-number-raise = 0.5\mm + footnote-auto-numbering = ##t + footnote-numbering-function = #numbered-footnotes + reset-footnotes-on-new-page = ##t %% %% Page numbering diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index 65b59bb01b..4545e5bd9f 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -884,6 +884,7 @@ (footnote-text . ,(grob::calc-property-by-copy 'footnote-text)) (stencil . ,ly:balloon-interface::print) (text . ,(grob::calc-property-by-copy 'text)) + (X-extent . #f) (Y-extent . #f) (X-offset . ,(grob::calc-property-by-copy 'X-offset)) (Y-offset . ,(grob::calc-property-by-copy 'Y-offset)) @@ -901,6 +902,7 @@ (spanner-placement . ,LEFT) (stencil . ,ly:balloon-interface::print-spanner) (text . ,(grob::calc-property-by-copy 'text)) + (X-extent . #f) (Y-extent . #f) (X-offset . ,(grob::calc-property-by-copy 'X-offset)) (Y-offset . ,(grob::calc-property-by-copy 'Y-offset)) diff --git a/scm/define-markup-commands.scm b/scm/define-markup-commands.scm index cf3417d3b5..38bf9623dc 100644 --- a/scm/define-markup-commands.scm +++ b/scm/define-markup-commands.scm @@ -1897,16 +1897,77 @@ returns an empty markup. (define-markup-command (footnote layout props mkup note) (markup? markup?) #:category other - "Have footnote @var{note} act as an annotation to the markup @var{mkup}." - (ly:stencil-combine-at-edge - (interpret-markup layout props mkup) - X - RIGHT + #:properties ((raise 0.5) + (padding 0.0)) + "Have footnote @var{note} act as an annotation to the markup @var{mkup}. + +@lilypond[verbatim,quote] +\\markup { + \\footnote a b + \\override #'(padding . 0.2) + \\footnote c d +} +@end lilypond" + (let* ((markup-stencil (interpret-markup layout props mkup)) + (auto-numbering (ly:output-def-lookup layout + 'footnote-auto-numbering)) + (footnote-hash (gensym "footnote")) + (stencil-seed 0) + (gauge-stencil (if auto-numbering + (interpret-markup + layout + props + ((ly:output-def-lookup + layout + 'footnote-numbering-function) + stencil-seed)) + empty-stencil)) + (x-ext (if auto-numbering + (ly:stencil-extent gauge-stencil X) + '(0 . 0))) + (y-ext (if auto-numbering + (ly:stencil-extent gauge-stencil Y) + '(0 . 0))) + (footnote-number + (if auto-numbering + `(delay-stencil-evaluation + ,(delay + (ly:stencil-expr + (let* ((table + (ly:output-def-lookup layout + 'number-footnote-table)) + (footnote-stencil (if (list? table) + (assoc-get footnote-hash + table) + empty-stencil)) + (footnote-stencil (if (ly:stencil? footnote-stencil) + footnote-stencil + (begin + (ly:programming-error +"Cannot find correct footnote for a markup object.") + empty-stencil))) + (gap (- (interval-length x-ext) + (interval-length + (ly:stencil-extent footnote-stencil X)))) + (y-trans (- (+ (cdr y-ext) + raise) + (cdr (ly:stencil-extent footnote-stencil + Y))))) + (ly:stencil-translate footnote-stencil + (cons gap y-trans)))))) + '())) + (main-stencil (ly:stencil-combine-at-edge + markup-stencil + X + RIGHT + (ly:make-stencil footnote-number x-ext y-ext) + padding))) + (ly:stencil-add + main-stencil (ly:make-stencil - `(footnote ,(interpret-markup layout props note)) + `(footnote ,footnote-hash ,(interpret-markup layout props note)) '(0 . 0) - '(0 . 0)) - 0.0)) + '(0 . 0))))) (define-markup-command (override layout props new-prop arg) (pair? markup?) @@ -3428,7 +3489,7 @@ a column containing several lines of text. (parenthesize-stencil markup half-thickness scaled-width angularity padding))) - + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Delayed markup evaluation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/scm/lily-library.scm b/scm/lily-library.scm index ede65ff5e2..34c9cb3820 100644 --- a/scm/lily-library.scm +++ b/scm/lily-library.scm @@ -746,6 +746,23 @@ Handy for debugging, possibly turned off." (reverse matches)) +(define-public (random-string pool n) + "Produces a random lowercase string of length n" + (define (helper alphabet out num) + (let ((rand (random (string-length pool)))) + (if (< num 1) + out + (helper alphabet + (string-concatenate `(,out + ,(substring alphabet + rand + (+ 1 rand)))) + (- num 1))))) + (helper pool "" n)) + +(define-public (random-lowercase-string n) + (random-string "abcdefghijklmnopqrstuvwxyz" n)) + ;;;;;;;;;;;;;;;; ;; other diff --git a/scm/output-lib.scm b/scm/output-lib.scm index 5d4bdb1b61..025590e5ba 100644 --- a/scm/output-lib.scm +++ b/scm/output-lib.scm @@ -467,6 +467,25 @@ and duration-log @var{log}." (+ c0 p)))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; annotations + +(define-public (numbered-footnotes int) + (markup #:tiny (number->string (+ 1 int)))) + +(define-public (symbol-footnotes int) + (define (helper symbols out idx n) + (if (< n 1) + out + (helper symbols + (string-append out (list-ref symbols idx)) + idx + (- n 1)))) + (markup #:tiny (helper '("*" "†" "‡" "§" "¶") + "" + (remainder int 5) + (+ 1 (quotient int 5))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; accidentals -- 2.39.2