--- /dev/null
+\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 |
+}}
--- /dev/null
+\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 |
+}}
#(set-default-paper-size "a6")
+\paper {
+ footnote-auto-numbering = ##f
+}
+
\book {
\new Staff \with { \consists "Footnote_engraver" }
#(set-default-paper-size "a6")
-\paper { ragged-last-bottom = ##f }
+\paper {
+ ragged-last-bottom = ##f
+ footnote-auto-numbering = ##f
+}
\book {
texidoc = "Lilypond does footnotes."
}
+\paper {
+ footnote-auto-numbering = ##f
+}
+
#(set-default-paper-size "a6")
\book {
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
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);
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;
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;
vsize system_count_;
Real footnote_separator_stencil_height_;
Real footnote_padding_;
+ Real footnote_number_raise_;
Real footnote_footer_padding_;
int orphan_penalty_;
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 */
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);
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);
int get_rank () const;
vector<Stencil *> get_footnotes_in_range (vsize st, vsize end);
void get_footnote_grobs_in_range (vector<Grob *> &out, vsize st, vsize end);
- Stencil make_footnote_stencil (Real padding);
+ vector<Grob *> *footnote_grobs ();
+ vsize num_footnotes ();
void do_break_substitution_and_fixup_refpoints ();
void post_processing ();
void populate_footnote_grob_vector ();
}
return font_list;
-}
+}
\ No newline at end of file
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"));
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
{
}
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;
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);
// 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;
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))
#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<System *> (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<SCM> footnote_number_markups; // Holds the numbering markups.
+ vector<Stencil *> 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<System *> (g);
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<Spanner *>(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<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 = 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");
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;
footnotes_found = true;
}
}
-
+
if (footnotes_found)
{
Stencil *separator = get_footnote_separator_stencil (pb->paper_);
}
}
-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);
{
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);
// 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;
}
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);
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);
*/
#include "paper-system.hh"
+#include "international.hh"
#include "item.hh"
Prob *
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;
}
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<Spanner *>(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<Grob *>*
+System::footnote_grobs ()
+{
+ return &footnote_grobs_;
}
void
#(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)
'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."))
%%
%% 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
(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))
(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))
(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?)
(parenthesize-stencil
markup half-thickness scaled-width angularity padding)))
-\f
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Delayed markup evaluation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(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
(+ 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