]> git.donarmstrong.com Git - lilypond.git/blob - lily/tuplet-bracket.cc
Fix some bugs in the dynamic engraver and PostScript backend
[lilypond.git] / lily / tuplet-bracket.cc
1 /*
2   tuplet-bracket.cc -- implement Tuplet_bracket
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2006 Jan Nieuwenhuizen <janneke@gnu.org>
7   Han-Wen Nienhuys <hanwen@xs4all.nl>
8 */
9
10 /*
11   TODO:
12
13   - tuplet bracket should probably be subject to the same rules as
14   beam sloping/quanting.
15
16   - There is no support for kneed brackets, or nested brackets.
17
18   - number placement for parallel beams should be much more advanced:
19   for sloped beams some extra horizontal offset must be introduced.
20
21   - number placement is usually done over the center note, not the
22   graphical center.
23 */
24
25 /*
26   TODO: quantise, we don't want to collide with staff lines.
27   (or should we be above staff?)
28
29   todo: handle breaking elegantly.
30 */
31
32
33 #include "tuplet-bracket.hh"
34 #include "line-interface.hh"
35 #include "beam.hh"
36 #include "warn.hh"
37 #include "output-def.hh"
38 #include "font-interface.hh"
39 #include "text-interface.hh"
40 #include "stem.hh"
41 #include "note-column.hh"
42 #include "pointer-group-interface.hh"
43 #include "directional-element-interface.hh"
44 #include "spanner.hh"
45 #include "staff-symbol-referencer.hh"
46 #include "lookup.hh"
47 #include "paper-column.hh"
48 #include "moment.hh"
49
50 static Item *
51 get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
52 {
53   Spanner *me = dynamic_cast<Spanner *> (me_grob);
54   Item *g = me->get_bound (hdir);
55   if (Note_column::has_interface (g)
56       && Note_column::get_stem (g)
57       && Note_column::dir (g) == my_dir)
58     g = Note_column::get_stem (g);
59
60   return g;
61 }
62
63
64 void
65 flatten_number_pair_property (Grob *me,
66                               Direction xdir,  SCM sym)
67 {
68   Drul_array<Real> zero (0, 0);
69   Drul_array<Real> pair
70     = robust_scm2drul (me->internal_get_property (sym), zero);
71   pair[xdir] = 0.0;
72   
73   me->internal_set_property (sym, ly_interval2scm (pair));
74 }
75
76
77 Grob *
78 Tuplet_bracket::parallel_beam (Grob *me_grob, vector<Grob*> const &cols, bool *equally_long)
79 {
80   Spanner *me = dynamic_cast<Spanner *> (me_grob);
81
82   if (me->get_bound (LEFT)->break_status_dir ()
83       || me->get_bound (RIGHT)->break_status_dir ())
84     return 0;
85
86   Drul_array<Grob*> stems (Note_column::get_stem (cols[0]),
87                            Note_column::get_stem (cols.back ()));
88
89   if (dynamic_cast<Item*> (stems[RIGHT])->get_column ()
90       != me->get_bound (RIGHT)->get_column())
91     return 0;
92
93   Drul_array<Grob*> beams;
94   Direction d = LEFT;
95   do {
96     beams[d] = stems[d] ? Stem::get_beam (stems[d]) : 0;
97   } while (flip (&d) != LEFT);
98   
99   *equally_long = false;
100   if (! (beams[LEFT] && (beams[LEFT] == beams[RIGHT]) && !me->is_broken ()))
101     return 0;
102
103   extract_grob_set (beams[LEFT], "stems", beam_stems);
104   if (beam_stems.size () == 0)
105     {
106       programming_error ("beam under tuplet bracket has no stems");
107       *equally_long = 0;
108       return 0;
109     }
110
111   *equally_long =
112     (beam_stems[0] == stems[LEFT] && beam_stems.back () == stems[RIGHT]);
113   return beams[LEFT];
114 }
115
116
117 MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_connect_to_neighbors,1);
118 SCM
119 Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
120 {
121   Spanner *me = unsmob_spanner (smob);
122
123   Direction dir = get_grob_direction (me); 
124   Drul_array<Item *> bounds (get_x_bound_item (me, LEFT, dir),
125                              get_x_bound_item (me, RIGHT, dir));
126   
127   Drul_array<bool> connect_to_other (false, false);
128   Direction d = LEFT;
129   do
130     {
131       Direction break_dir = bounds[d]->break_status_dir ();
132       
133       Spanner *orig_spanner = dynamic_cast<Spanner*> (me->original ());
134
135       vsize neighbor_idx = me->get_break_index () - break_dir;
136       if (break_dir
137           && d == RIGHT
138           && neighbor_idx < orig_spanner->broken_intos_.size ())
139         {
140           Grob *neighbor = orig_spanner->broken_intos_[neighbor_idx];
141
142           /* trigger possible suicide*/
143           (void) neighbor->get_property ("positions");
144         }
145
146       connect_to_other[d]
147         = (break_dir
148            && neighbor_idx < orig_spanner->broken_intos_.size ()
149            && orig_spanner->broken_intos_[neighbor_idx]->is_live ());
150     }
151   while (flip (&d) != LEFT);
152
153
154   if (connect_to_other[LEFT] || connect_to_other[RIGHT])
155     return scm_cons (scm_from_bool (connect_to_other[LEFT]),
156                      scm_from_bool (connect_to_other[RIGHT]));
157                      
158   return SCM_EOL;
159 }
160
161 Grob* 
162 Tuplet_bracket::get_common_x (Spanner *me)
163 {
164   extract_grob_set (me, "note-columns", columns);
165
166   Grob * commonx = common_refpoint_of_array (columns, me, X_AXIS);
167   commonx = commonx->common_refpoint (me->get_bound (LEFT), X_AXIS);
168   commonx = commonx->common_refpoint (me->get_bound (RIGHT), X_AXIS);
169
170   return commonx;
171 }
172   
173 MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_control_points,1)
174 SCM
175 Tuplet_bracket::calc_control_points (SCM smob)
176 {
177   Spanner *me = unsmob_spanner (smob);
178
179   extract_grob_set (me, "note-columns", columns);
180
181   SCM scm_positions = me->get_property ("positions");
182   if (!me->is_live ())
183     return SCM_EOL;
184   
185   if (!scm_is_pair (scm_positions))
186     programming_error ("Positions should be number pair");
187     
188   Drul_array<Real> positions
189     = robust_scm2drul (scm_positions, Drul_array<Real> (0,0));
190
191   Grob *commonx = get_common_x (me);
192   Direction dir = get_grob_direction (me);
193
194   Drul_array<Item *> bounds;
195   bounds[LEFT] = get_x_bound_item (me, LEFT, dir);
196   bounds[RIGHT] = get_x_bound_item (me, RIGHT, dir);
197
198   Drul_array<bool> connect_to_other =
199     robust_scm2booldrul (me->get_property ("connect-to-neighbor"),
200                          Drul_array<bool> (false, false));
201   
202     
203   Interval x_span;
204   Direction d = LEFT;
205   do
206     {
207       x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[d];
208
209       if (connect_to_other[d])
210         {
211           Interval overshoot (robust_scm2drul (me->get_property ("break-overshoot"),
212                                                Interval (-0.5, 0.0)));
213
214           if (d == RIGHT)
215             x_span[d] += d * overshoot[d];
216           else
217             x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[RIGHT]
218               - overshoot[LEFT];
219         }
220       
221       else if (d == RIGHT
222                && (columns.empty ()
223                    || (bounds[d]->get_column ()
224                        != dynamic_cast<Item *> (columns.back ())->get_column ())))
225         {
226           /*
227             We're connecting to a column, for the last bit of a broken
228             fullLength bracket.
229             
230             TODO: make padding tunable?
231           */
232           Real padding = 1.0;
233
234           if (bounds[d]->break_status_dir ())
235             padding = 0.0;
236           
237           x_span[d]
238             = robust_relative_extent (bounds[d], commonx, X_AXIS) [LEFT]
239             - padding;
240         }
241     }
242   while (flip (&d) != LEFT);
243
244   
245   
246   x_span -= me->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS);
247   return scm_list_2 (ly_offset2scm (Offset (x_span[LEFT], positions[LEFT])),
248                      ly_offset2scm (Offset (x_span[RIGHT], positions[RIGHT])));
249 }
250
251 /*
252   TODO:
253
254   in the case that there is no bracket, but there is a (single) beam,
255   follow beam precisely for determining tuplet number location.
256 */
257 MAKE_SCHEME_CALLBACK (Tuplet_bracket, print, 1);
258 SCM
259 Tuplet_bracket::print (SCM smob)
260 {
261   Spanner *me = unsmob_spanner (smob);
262   Stencil mol;
263
264   extract_grob_set (me, "note-columns", columns);
265   bool equally_long = false;
266   Grob *par_beam = parallel_beam (me, columns, &equally_long);
267   
268   bool bracket_visibility = !(par_beam && equally_long);
269   /*
270     Fixme: the type of this prop is sucky.
271   */
272   SCM bracket = me->get_property ("bracket-visibility");
273   if (scm_is_bool (bracket))
274     bracket_visibility = ly_scm2bool (bracket);
275   else if (bracket == ly_symbol2scm ("if-no-beam"))
276     bracket_visibility = !par_beam;
277
278   
279   SCM cpoints =  me->get_property ("control-points");
280   if (scm_ilength (cpoints) < 2)
281     {
282       me->suicide ();
283       return SCM_EOL;
284     }
285   
286   Drul_array<Offset> points;
287   points[LEFT] = ly_scm2offset (scm_car (cpoints));
288   points[RIGHT] = ly_scm2offset (scm_cadr (cpoints));
289   
290   Interval x_span (points[LEFT][X_AXIS], points[RIGHT][X_AXIS]);
291   Drul_array<Real> positions (points[LEFT][Y_AXIS], points[RIGHT][Y_AXIS]);
292
293
294
295   Output_def *pap = me->layout ();
296
297   Grob *number_grob = unsmob_grob (me->get_object ("tuplet-number"));
298   
299   /*
300     No bracket when it would be smaller than the number.
301   */
302   Real gap = 0.;
303   if (bracket_visibility && number_grob)
304     {
305       Interval ext = number_grob->extent (number_grob, X_AXIS);
306       if (!ext.is_empty ())
307         {
308           gap = ext.length () + 1.0;
309       
310           if (0.75 * x_span.length () < gap)
311             bracket_visibility = false;
312         }
313     }
314
315   if (bracket_visibility)
316     {
317       Drul_array<Real> zero (0, 0);
318       Real ss = Staff_symbol_referencer::staff_space (me);
319       Drul_array<Real> height
320         = robust_scm2drul (me->get_property ("edge-height"), zero);
321       Drul_array<Real> flare
322         = robust_scm2drul (me->get_property ("bracket-flare"), zero);
323       Drul_array<Real> shorten
324         = robust_scm2drul (me->get_property ("shorten-pair"), zero);
325       Drul_array<Stencil> edge_stencils;
326
327       Direction dir = get_grob_direction (me);
328       
329       scale_drul (&height, -ss * dir);
330       scale_drul (&flare, ss);
331       scale_drul (&shorten, ss);
332
333       Drul_array<bool> connect_to_other =
334         robust_scm2booldrul (me->get_property ("connect-to-neighbor"),
335                              Drul_array<bool> (false, false));
336
337       Direction d = LEFT;
338       do
339         {
340           if (connect_to_other[d])
341             {
342               height[d] = 0.0;
343               flare[d] = 0.0;
344               shorten[d] = 0.0;
345
346               SCM edge_text = me->get_property ("edge-text");
347
348               if (scm_is_pair (edge_text))
349                 {
350                   SCM properties = Font_interface::text_font_alist_chain (me);
351                   SCM text = index_get_cell (edge_text, d);
352                   if (Text_interface::is_markup (text))
353                     {
354                       SCM t = Text_interface::interpret_markup (pap->self_scm (),
355                                                                 properties, text);
356
357                       Stencil *edge_text = unsmob_stencil (t);
358                       edge_text->translate_axis (x_span[d] - x_span[LEFT], X_AXIS);
359                       edge_stencils[d] = *edge_text;
360                     }
361                 }
362             }
363         }
364       while (flip (&d) != LEFT);
365
366       Stencil brack = make_bracket (me, Y_AXIS,
367                                     points[RIGHT] - points[LEFT],
368                                     height,
369                                     /*
370                                       0.1 = more space at right due to italics
371                                       TODO: use italic correction of font.
372                                     */
373                                     Interval (-0.5, 0.5) * gap + 0.1,
374                                     flare, shorten);
375
376       do
377         {
378           if (!edge_stencils[d].is_empty ())
379             brack.add_stencil (edge_stencils[d]);
380         }
381       while (flip (&d) != LEFT);
382
383       mol.add_stencil (brack);
384     }
385
386   mol.translate (points[LEFT]);
387   return mol.smobbed_copy ();
388 }
389
390 /*
391   should move to lookup?
392
393   TODO: this will fail for very short (shorter than the flare)
394   brackets.
395 */
396 Stencil
397 Tuplet_bracket::make_bracket (Grob *me, // for line properties.
398                               Axis protusion_axis,
399                               Offset dz,
400                               Drul_array<Real> height,
401                               Interval gap,
402                               Drul_array<Real> flare,
403                               Drul_array<Real> shorten)
404 {
405   Drul_array<Offset> corners (Offset (0, 0), dz);
406
407   Real length = dz.length ();
408   Drul_array<Offset> gap_corners;
409
410   Axis bracket_axis = other_axis (protusion_axis);
411
412   Drul_array<Offset> straight_corners = corners;
413
414   Direction d = LEFT;
415   do
416     straight_corners[d] += -d * shorten[d] / length * dz;
417   while (flip (&d) != LEFT)
418     ;
419
420   if (gap.is_empty ())
421     gap = Interval (0, 0);
422   do
423     gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
424   while (flip (&d) != LEFT)
425     ;
426
427   Drul_array<Offset> flare_corners = straight_corners;
428   do
429     {
430       flare_corners[d][bracket_axis] = straight_corners[d][bracket_axis];
431       flare_corners[d][protusion_axis] += height[d];
432       straight_corners[d][bracket_axis] += -d * flare[d];
433     }
434   while (flip (&d) != LEFT);
435
436   Stencil m;
437   do
438     {
439       m.add_stencil (Line_interface::line (me, straight_corners[d],
440                                            gap_corners[d]));
441
442       m.add_stencil (Line_interface::line (me, straight_corners[d],
443                                            flare_corners[d]));
444     }
445   while (flip (&d) != LEFT);
446
447   return m;
448 }
449
450 void
451 Tuplet_bracket::get_bounds (Grob *me, Grob **left, Grob **right)
452 {
453   extract_grob_set (me, "note-columns", columns);
454   vsize l = 0;
455   while (l < columns.size () && Note_column::has_rests (columns[l]))
456     l++;
457
458   vsize r = columns.size ();
459   while (r > l && Note_column::has_rests (columns[r-1]))
460     r--;
461
462   *left = *right = 0;
463
464   if (l < r)
465     {
466       *left = columns[l];
467       *right = columns[r-1];
468     }
469 }
470
471 /*
472   use first -> last note for slope, and then correct for disturbing
473   notes in between.  */
474 void
475 Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
476 {
477   Spanner *me = dynamic_cast<Spanner *> (me_grob);
478
479   extract_grob_set (me, "note-columns", columns);
480   extract_grob_set (me, "tuplets", tuplets);
481
482   Grob *commony = common_refpoint_of_array (columns, me, Y_AXIS);
483   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
484   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
485     commony = st->common_refpoint (commony, Y_AXIS);
486
487   Grob *commonx = get_common_x (me);
488   commonx = common_refpoint_of_array (tuplets, commonx, Y_AXIS);
489
490   Interval staff;
491   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
492     {
493       Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
494       if  (pad >= 0.0)
495         {
496           staff = st->extent (commony, Y_AXIS);
497           staff.widen (pad);
498         }
499     }
500   
501   Direction dir = get_grob_direction (me);
502
503   /*
504     Use outer non-rest columns to determine slope
505   */
506   Grob *left_col = 0;
507   Grob *right_col = 0;
508   get_bounds (me, &left_col, &right_col);
509   if (left_col && right_col)
510     {
511       Interval rv = right_col->extent (commony, Y_AXIS);
512       Interval lv = left_col->extent (commony, Y_AXIS);
513       rv.unite (staff);
514       lv.unite (staff);
515       Real graphical_dy = rv[dir] - lv[dir];
516
517       Slice ls = Note_column::head_positions_interval (left_col);
518       Slice rs = Note_column::head_positions_interval (right_col);
519
520       Interval musical_dy;
521       musical_dy[UP] = rs[UP] - ls[UP];
522       musical_dy[DOWN] = rs[DOWN] - ls[DOWN];
523       if (sign (musical_dy[UP]) != sign (musical_dy[DOWN]))
524         *dy = 0.0;
525       else if (sign (graphical_dy) != sign (musical_dy[DOWN]))
526         *dy = 0.0;
527       else
528         *dy = graphical_dy;
529     }
530   else
531     *dy = 0;
532
533   *offset = -dir * infinity_f;
534
535   Item *lgr = get_x_bound_item (me, LEFT, dir);
536   Item *rgr = get_x_bound_item (me, RIGHT, dir);
537   Real x0 = robust_relative_extent (lgr, commonx, X_AXIS)[LEFT];
538   Real x1 = robust_relative_extent (rgr, commonx, X_AXIS)[RIGHT];
539
540   vector<Offset> points;
541   points.push_back (Offset (x0 - x0, staff[dir]));
542   points.push_back (Offset (x1 - x0, staff[dir]));
543
544   for (vsize i = 0; i < columns.size (); i++)
545     {
546       Interval note_ext = columns[i]->extent (commony, Y_AXIS);
547       Real notey = note_ext[dir] - me->relative_coordinate (commony, Y_AXIS);
548
549       Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
550       points.push_back (Offset (x, notey));
551     }
552
553   /*
554     This is a slight hack. We compute two encompass points from the
555     bbox of the smaller tuplets.
556
557     We assume that the smaller bracket is 1.0 space high.
558   */
559   Real ss = Staff_symbol_referencer::staff_space (me);
560   for (vsize i = 0; i < tuplets.size (); i++)
561     {
562       Interval tuplet_x (tuplets[i]->extent (commonx, X_AXIS));
563       Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
564
565       Direction d = LEFT;
566       Drul_array<Real> positions = ly_scm2realdrul (tuplets[i]->get_property ("positions"));
567
568       
569       Real other_dy = positions[RIGHT] - positions[LEFT];
570
571       do
572         {
573           Real y
574             = tuplet_y.linear_combination (d * sign (other_dy));
575
576           /*
577             We don't take padding into account for nested tuplets.
578             the edges can come very close to the stems, likewise for
579             nested tuplets?
580           */
581
582           points.push_back (Offset (tuplet_x[d] - x0, y));
583         }
584       while (flip (&d) != LEFT);
585     }
586
587   Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
588   for (vsize i = 0; i < points.size (); i++)
589     {
590       Real x = points[i][X_AXIS];
591       Real tuplety = (*dy) * x * factor;
592
593       if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
594         *offset = points[i][Y_AXIS] - tuplety;
595     }
596
597   *offset += scm_to_double (me->get_property ("padding")) * dir;
598
599   /*
600     horizontal brackets should not collide with staff lines.
601
602     Kind of pointless since we put them outside the staff anyway, but
603     let's leave code for the future when possibly allow them to move
604     into the staff once again.
605   */
606   if (*dy == 0
607       && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
608     {
609       // quantize, then do collision check.
610       *offset *= 2 / ss;
611
612       *offset = rint (*offset);
613       if (Staff_symbol_referencer::on_line (me, (int) rint (*offset)))
614         *offset += dir;
615
616       *offset *= 0.5 * ss;
617     }
618 }
619
620
621 MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_direction, 1);
622 SCM
623 Tuplet_bracket::calc_direction (SCM smob)
624 {
625   Grob *me = unsmob_grob (smob);
626   Direction dir = Tuplet_bracket::get_default_dir (me);
627   return scm_from_int (dir);
628 }
629
630 MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_positions, 1);
631 SCM
632 Tuplet_bracket::calc_positions (SCM smob)
633 {
634   Spanner *me = unsmob_spanner (smob);
635   extract_grob_set (me, "note-columns", columns);
636
637   /*
638     Don't print if it doesn't span time.
639    */
640   if (robust_scm2moment (me->get_bound (LEFT)->get_column ()->get_property ("when"), Moment (0))
641       == robust_scm2moment (me->get_bound (RIGHT)->get_column ()->get_property ("when"), Moment (0)))
642     {
643       me->suicide ();
644       return SCM_EOL;
645     }
646
647   
648   Direction dir = get_grob_direction (me);
649   bool equally_long = false;
650   Grob *par_beam = parallel_beam (me, columns, &equally_long);
651
652   /*
653     We follow the beam only if there is one, and we are next to it.
654   */
655   Real dy = 0.0;
656   Real offset = 0.0;
657   if (!par_beam
658       || get_grob_direction (par_beam) != dir)
659     calc_position_and_height (me, &offset, &dy);
660   else
661     {
662       SCM ps = par_beam->get_property ("positions");
663
664       Real lp = scm_to_double (scm_car (ps));
665       Real rp = scm_to_double (scm_cdr (ps));
666
667       Real ss = Staff_symbol_referencer::staff_space (me);
668       
669       offset = lp + dir * (0.5 + scm_to_double (me->get_property ("padding")));
670       dy = (rp - lp);
671
672       dy *= ss;
673       offset *= ss;     
674     }
675
676   
677   SCM x = scm_cons (scm_from_double (offset),
678                     scm_from_double (offset + dy));
679   
680   return x;
681 }
682
683 /*
684   similar to beam ?
685 */
686 Direction
687 Tuplet_bracket::get_default_dir (Grob *me)
688 {
689   Drul_array<int> dirs (0, 0);
690   extract_grob_set (me, "note-columns", columns);
691   for (vsize i = 0; i < columns.size (); i++)
692     {
693       Grob *nc = columns[i];
694       Direction d = Note_column::dir (nc);
695       if (d)
696         dirs[d]++;
697     }
698
699   return dirs[UP] >= dirs[DOWN] ? UP : DOWN;
700 }
701
702 void
703 Tuplet_bracket::add_column (Grob *me, Item *n)
704 {
705   Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
706   add_bound_item (dynamic_cast<Spanner *> (me), n);
707 }
708
709 void
710 Tuplet_bracket::add_tuplet_bracket (Grob *me, Grob *bracket)
711 {
712   Pointer_group_interface::add_grob (me, ly_symbol2scm ("tuplets"), bracket);
713 }
714
715 ADD_INTERFACE (Tuplet_bracket,
716                "tuplet-bracket-interface",
717                "A bracket with a number in the middle, used for tuplets. "
718                "When the bracket spans  a line break, the value of "
719                "@code{break-overshoot} determines how far it extends "
720                "beyond the staff. "
721                "At a line break, the markups in the @code{edge-text} are printed "
722                "at the edges. ",
723
724
725                /* properties */
726                "bracket-flare "
727                "bracket-visibility "
728                "break-overshoot "
729                "connect-to-neighbor "
730                "control-points "
731                "direction "
732                "edge-height "
733                "edge-text "
734                "gap "
735                "positions "
736                "note-columns "
737                "padding "
738                "tuplet-number "
739                "shorten-pair "
740                "staff-padding "
741                "thickness "
742                "tuplets ");
743
744