]> git.donarmstrong.com Git - lilypond.git/blob - lily/vaticana-ligature-engraver.cc
53bec433d7c196be7b7fb5a9728ac273cd6e8685
[lilypond.git] / lily / vaticana-ligature-engraver.cc
1 /*
2   vaticana-ligature-engraver.cc -- implement Vaticana_ligature_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2003--2008 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
9 #include "gregorian-ligature-engraver.hh"
10
11 #include "font-interface.hh"
12 #include "gregorian-ligature.hh"
13 #include "international.hh"
14 #include "output-def.hh"
15 #include "paper-column.hh"
16 #include "separation-item.hh"
17 #include "spanner.hh"
18 #include "staff-symbol-referencer.hh"
19 #include "stream-event.hh"
20 #include "vaticana-ligature.hh"
21 #include "warn.hh"
22 #include "dot-column.hh"
23 #include "rhythmic-head.hh"
24 #include "pitch.hh"
25 #include "translator.icc"
26
27 /*
28  * This class implements the notation specific aspects of Vaticana
29  * style ligatures for Gregorian chant notation.
30  */
31
32 /*
33  * TODO: Maybe move handling of dots/mora to
34  * Gregorian_ligature_engraver?  It's probably common for all types of
35  * Gregorian chant notation that have dotted notes.
36  *
37  * FIXME: The horizontal alignment of the mora column is bad (too far
38  * to the left), if the last dotted note is not the last primitive in
39  * the ligature.  Fortunately, in practice this bug should have no
40  * negative impact, since dotted notes appear within a ligature
41  * usually always at the end of the ligature, such that the bug never
42  * should apply for valid ligatures.
43  *
44  * TODO: Graduale Triplex, tempus per annum, hebdomada septima,
45  * alleluia (page 280) shows a counter-example for collecting dots
46  * always in a single column behind the ligature.  Maybe only the last
47  * two dots in a ligature should be collected and all other dots put
48  * behind or on top of the head?
49  */
50 class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
51 {
52
53 private:
54   static bool
55   need_extra_horizontal_space (int prev_prefix_set, int prefix_set,
56                                int context_info, int delta_pitch);
57   bool is_stacked_head (int prefix_set,
58                         int context_info);
59   Real align_heads (vector<Grob_info> primitives,
60                     Real flexa_width,
61                     Real thickness);
62   void check_for_prefix_loss (Item *primitive);
63   void check_for_ambiguous_dot_pitch (Grob_info primitive);
64   void add_mora_column (Paper_column *column);
65   vector<Grob_info> augmented_primitives_;
66
67 public:
68   TRANSLATOR_DECLARATIONS (Vaticana_ligature_engraver);
69
70 protected:
71   virtual Spanner *create_ligature_spanner ();
72   virtual void transform_heads (Spanner *ligature,
73                                 vector<Grob_info> primitives);
74   DECLARE_TRANSLATOR_LISTENER (pes_or_flexa);
75   DECLARE_TRANSLATOR_LISTENER (ligature);
76 };
77
78 IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, pes_or_flexa);
79 void
80 Vaticana_ligature_engraver::listen_pes_or_flexa (Stream_event *ev)
81 {
82   Gregorian_ligature_engraver::listen_pes_or_flexa (ev);
83 }
84
85 IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, ligature);
86 void
87 Vaticana_ligature_engraver::listen_ligature (Stream_event *ev)
88 {
89   Ligature_engraver::listen_ligature (ev);
90 }
91
92 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
93 {
94   brew_ligature_primitive_proc = 
95     Vaticana_ligature::brew_ligature_primitive_proc;
96   augmented_primitives_.clear ();
97 }
98
99 Spanner *
100 Vaticana_ligature_engraver::create_ligature_spanner ()
101 {
102   return make_spanner ("VaticanaLigature", SCM_EOL);
103 }
104
105 bool
106 Vaticana_ligature_engraver::is_stacked_head (int prefix_set,
107                                              int context_info)
108 {
109   bool is_stacked;
110
111   // upper head of pes is stacked upon lower head of pes ...
112   is_stacked = context_info & PES_UPPER;
113
114   // ... unless this note starts a flexa
115   if (context_info & FLEXA_LEFT)
116     is_stacked = false;
117
118   // ... or another pes
119   if (context_info & PES_LOWER)
120     is_stacked = false;
121
122   // ... or the previous note is a semivocalis or inclinatum
123   if (context_info & AFTER_DEMINUTUM)
124     is_stacked = false;
125
126   // auctum head is never stacked upon preceding note
127   if (prefix_set & AUCTUM)
128     is_stacked = false;
129
130   // virga is never stacked upon preceding note
131   if (prefix_set & VIRGA)
132     is_stacked = false;
133
134   // oriscus is never stacked upon preceding note
135   if (prefix_set & ORISCUS)
136     is_stacked = false;
137
138   if ((prefix_set & DEMINUTUM)
139       && ! (prefix_set & INCLINATUM)
140       && (context_info & FLEXA_RIGHT))
141     is_stacked = true; // semivocalis head of deminutus form
142
143   return is_stacked;
144 }
145
146 /*
147  * When aligning the heads, sometimes extra space is needed, e.g. to
148  * avoid clashing with the appendix of an adjacent notehead or with an
149  * adjacent notehead itself if it has the same pitch.  Extra space is
150  * added at most once between to heads.
151  */
152 bool
153 Vaticana_ligature_engraver::need_extra_horizontal_space (int prev_prefix_set, int prefix_set,
154                                                          int context_info, int delta_pitch)
155 {
156   if (prev_prefix_set & VIRGA)
157     /*
158      * After a virga, make a an additional small space such that the
159      * appendix on the right side of the head does not touch the
160      * following head.
161      */
162     return true;
163
164   if ((prefix_set & INCLINATUM)
165       && ! (prev_prefix_set & INCLINATUM))
166     /*
167      * Always start a series of inclinatum heads with an extra space.
168      */
169     return true;
170
171   if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
172     /*
173      * Before a flexa (but not within a torculus), make a an
174      * additional small space such that the appendix on the left side
175      * of the flexa does not touch the this head.
176      */
177     return true;
178
179   if (delta_pitch == 0)
180     /*
181      * If there are two adjacent noteheads with the same pitch, add
182      * additional small space between them, such that they do not
183      * touch each other.
184      */
185     return true;
186
187   return false;
188 }
189
190 Real
191 Vaticana_ligature_engraver::align_heads (vector<Grob_info> primitives,
192                                          Real flexa_width,
193                                          Real thickness)
194 {
195   if (!primitives.size ())
196     {
197       programming_error ("Vaticana_ligature: "
198                          "empty ligature [ignored]");
199       return 0.0;
200     }
201
202   /*
203    * The paper column where we put the whole ligature into.
204    */
205   Paper_column *column
206     = dynamic_cast<Item *> (primitives[0].grob ())->get_column ();
207
208   Real join_thickness
209     = thickness * column->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
210
211   /*
212    * Amount of extra space two put between some particular
213    * configurations of adjacent heads.
214    *
215    * TODO: make this a property of primtive grobs.
216    */
217   Real extra_space = 4.0 * join_thickness;
218
219   /*
220    * Keep track of the total width of the ligature.
221    */
222   Real ligature_width = 0.0;
223
224   Item *prev_primitive = 0;
225   int prev_prefix_set = 0;
226   for (vsize i = 0; i < primitives.size (); i++)
227     {
228       Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
229       int prefix_set
230         = scm_to_int (primitive->get_property ("prefix-set"));
231       int context_info
232         = scm_to_int (primitive->get_property ("context-info"));
233
234       /*
235        * Get glyph_name, delta_pitch and context_info for this head.
236        */
237
238       SCM glyph_name_scm = primitive->get_property ("glyph-name");
239       if (glyph_name_scm == SCM_EOL)
240         {
241           primitive->programming_error ("Vaticana_ligature:"
242                                         " undefined glyph-name ->"
243                                         " ignoring grob");
244           continue;
245         }
246       string glyph_name = ly_scm2string (glyph_name_scm);
247
248       int delta_pitch = 0;
249       if (prev_primitive) /* urgh, need prev_primitive only here */
250         {
251           SCM delta_pitch_scm = prev_primitive->get_property ("delta-position");
252           if (delta_pitch_scm != SCM_EOL)
253             delta_pitch = scm_to_int (delta_pitch_scm);
254           else
255             {
256               primitive->programming_error ("Vaticana_ligature:"
257                                             " delta-position undefined ->"
258                                             " ignoring grob");
259               continue;
260             }
261         }
262
263       /*
264        * Now determine width and x-offset of head.
265        */
266
267       Real head_width;
268       Real x_offset;
269
270       if (context_info & STACKED_HEAD)
271         {
272           /*
273            * This head is stacked upon the previous one; hence, it
274            * does not contribute to the total width of the ligature,
275            * and its width is assumed to be 0.0.  Moreover, it is
276            * shifted to the left by its width such that the right side
277            * of this and the other head are horizontally aligned.
278            */
279           head_width = 0.0;
280           x_offset = join_thickness
281             - Font_interface::get_default_font (primitive)->
282             find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
283         }
284       else if (glyph_name == "flexa" || glyph_name == "")
285         {
286           /*
287            * This head represents either half of a flexa shape.
288            * Hence, it is assigned half the width of this shape.
289            */
290           head_width = 0.5 * flexa_width;
291           x_offset = 0.0;
292         }
293       else
294         {
295           /*
296            * This is a regular head, placed right to the previous one.
297            * Retrieve its width from corresponding font.
298            */
299           head_width
300             = Font_interface::get_default_font (primitive)->
301             find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
302           x_offset = 0.0;
303         }
304
305       /*
306        * Save the head's final x-offset.
307        */
308       primitive->set_property ("x-offset",
309                                scm_from_double (x_offset));
310
311       /*
312        * If the head is the 2nd head of a pes or flexa (but not a
313        * flexa shape), mark this head to be joined with the left-side
314        * neighbour head (i.e. the previous head) by a vertical beam.
315        */
316       if ((context_info & PES_UPPER)
317           || ((context_info & FLEXA_RIGHT)
318               && ! (context_info & PES_LOWER)))
319         {
320           if (!prev_primitive)
321             {
322               primitive->programming_error ("vaticana ligature: add-join: "
323                                             "missing previous primitive");
324             }
325           else
326             {
327               prev_primitive->set_property ("add-join",
328                                             ly_bool2scm (true));
329
330               /*
331                * Create a small overlap of adjacent heads so that the join
332                * can be drawn perfectly between them.
333                */
334               ligature_width -= join_thickness;
335             }
336         }
337       else if (glyph_name == "")
338         {
339           /*
340            * This is the 2nd (virtual) head of flexa shape.  Join it
341            * tightly with 1st head, i.e. do *not* add additional
342            * space, such that next head will not be off from the flexa
343            * shape.
344            */
345         }
346
347       if (need_extra_horizontal_space (prev_prefix_set, prefix_set,
348                                        context_info, delta_pitch))
349         ligature_width += extra_space;
350
351       /*
352        * Horizontally line-up this head to form a ligature.
353        */
354       move_related_items_to_column (primitive, column, ligature_width);
355       ligature_width += head_width;
356
357       prev_primitive = primitive;
358       prev_prefix_set = prefix_set;
359     }
360
361   /*
362    * Add extra horizontal padding space after ligature, such that
363    * neighbouring ligatures do not touch each other.
364    */
365   ligature_width += extra_space;
366
367   return ligature_width;
368 }
369
370 /*
371  * Depending on the typographical features of a particular ligature
372  * style, some prefixes may be ignored.  In particular, if a curved
373  * flexa shape is produced, any prefixes to either of the two
374  * contributing heads that would select a head other than punctum, is
375  * by definition ignored.
376  *
377  * This function prints a warning, if the given primitive is prefixed
378  * such that a head other than punctum would be chosen, if this
379  * primitive were engraved as a stand-alone head.
380  */
381 void
382 Vaticana_ligature_engraver::check_for_prefix_loss (Item *primitive)
383 {
384   int prefix_set
385     = scm_to_int (primitive->get_property ("prefix-set"));
386   if (prefix_set & ~PES_OR_FLEXA)
387     {
388       string prefs = Gregorian_ligature::prefixes_to_str (primitive);
389       primitive->warning (_f ("ignored prefix (es) `%s' of this head according "
390                               "to restrictions of the selected ligature style",
391                               prefs.c_str ()));
392     }
393 }
394
395 void
396 Vaticana_ligature_engraver::add_mora_column (Paper_column *column)
397 {
398   if (augmented_primitives_.size () == 0) // no dot for column
399     return;
400   if (!column) // empty ligature???
401     {
402       augmented_primitives_[0].grob ()->
403         programming_error ("no paper column to add dot");
404       return;
405     }
406   Item *dotcol = make_item ("DotColumn", SCM_EOL);
407   dotcol->set_parent (column, X_AXIS);
408   for (vsize i = 0; i < augmented_primitives_.size (); i++)
409     {
410       Item *primitive =
411         dynamic_cast<Item *> (augmented_primitives_[i].grob ());
412       Item *dot = make_item ("Dots", primitive->self_scm ());
413       dot->set_property ("dot-count", scm_from_int (1));
414       dot->set_parent (primitive, Y_AXIS);
415       primitive->set_object ("dot", dot->self_scm ());
416       Dot_column::add_head (dotcol, primitive);
417
418       // FIXME: why isn't the dot picked up by Paper_column_engraver?
419       Separation_item::add_item (column, dot);
420     }
421 }
422
423 /*
424  * This function prints a warning, if the given primitive has the same
425  * pitch as at least one of the primitives already stored in the
426  * augmented_primitives_ array.
427  *
428  * The rationale of this check is, that, if there are two dotted
429  * primitives with the same pitch, then collecting all dots in a dot
430  * column behind the ligature leads to a notational ambiguity of to
431  * which head the corresponding dot refers.
432  *
433  * Such a case should be treated as a badly specified ligature.  The
434  * user should split the ligature to make the notation of dots
435  * unambiguous.
436  */
437 void
438 Vaticana_ligature_engraver::check_for_ambiguous_dot_pitch (Grob_info primitive)
439 {
440   // TODO: Fix performance, which is currently O (n^2) (since this
441   // method is called O (n) times and takes O (n) steps in the for
442   // loop), but could be O (n) (by replacing the for loop by e.g. a
443   // bitmask based O (1) test); where n=<number of primitives in the
444   // ligature> (which is typically small (n<10), though).
445   Stream_event *new_cause = primitive.event_cause ();
446   int new_pitch = unsmob_pitch (new_cause->get_property ("pitch"))->steps ();
447   for (vsize i = 0; i < augmented_primitives_.size (); i++)
448     {
449       Stream_event *cause = augmented_primitives_[i].event_cause ();
450       int pitch = unsmob_pitch (cause->get_property ("pitch"))->steps ();
451       if (pitch == new_pitch)
452         {
453           primitive.grob ()->
454             warning ("Ambiguous use of dots in ligature: there are "
455                      "multiple dotted notes with the same pitch.  "
456                      "The ligature should be split.");
457           return; // supress multiple identical warnings
458         }
459     }
460 }
461
462 void
463 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
464                                              vector<Grob_info> primitives)
465 {
466   Real flexa_width = robust_scm2double (ligature->get_property ("flexa-width"), 2);
467
468   Real thickness = robust_scm2double (ligature->get_property ("thickness"), 1);
469
470   Item *prev_primitive = 0;
471   int prev_prefix_set = 0;
472   int prev_context_info = 0;
473   int prev_delta_pitch = 0;
474   string prev_glyph_name = "";
475   augmented_primitives_.clear ();
476   for (vsize i = 0; i < primitives.size (); i++)
477     {
478       Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
479
480       int delta_pitch;
481       SCM delta_pitch_scm = primitive->get_property ("delta-position");
482       if (delta_pitch_scm != SCM_EOL)
483         delta_pitch = scm_to_int (delta_pitch_scm);
484       else
485         {
486           primitive->programming_error ("Vaticana_ligature:"
487                                         " delta-position undefined ->"
488                                         " ignoring grob");
489           continue;
490         }
491
492       /* retrieve & complete prefix_set and context_info */
493       int prefix_set
494         = scm_to_int (primitive->get_property ("prefix-set"));
495       int context_info
496         = scm_to_int (primitive->get_property ("context-info"));
497
498       if (Rhythmic_head::dot_count (primitive) > 0)
499         // remove dots from primitive and add remember primitive for
500         // creating a dot column
501         {
502           Rhythmic_head::get_dots (primitive)->set_property ("dot-count",
503                                                              scm_from_int (0));
504           // TODO: Maybe completely remove grob "Dots" (dots->suicide
505           // () ?) rather than setting property "dot-count" to 0.
506
507           check_for_ambiguous_dot_pitch (primitives[i]);
508           augmented_primitives_.push_back (primitives[i]);
509         }
510       else if (augmented_primitives_.size () > 0)
511         {
512           primitive->warning ("This ligature has a dotted head followed by "
513                               "a non-dotted head.  The ligature should be "
514                               "split after the last dotted head before "
515                               "this head.");
516         }
517
518       if (is_stacked_head (prefix_set, context_info))
519         {
520           context_info |= STACKED_HEAD;
521           primitive->set_property ("context-info",
522                                    scm_from_int (context_info));
523         }
524
525       /*
526        * Now determine which head to typeset (this is context sensitive
527        * information, since it depends on neighbouring heads; therefore,
528        * this decision must be made here in the engraver rather than in
529        * the backend).
530        */
531       string glyph_name;
532       if (prefix_set & VIRGA)
533         {
534           glyph_name = "vaticana.punctum";
535           primitive->set_property ("add-stem", ly_bool2scm (true));
536         }
537       else if (prefix_set & QUILISMA)
538         glyph_name = "vaticana.quilisma";
539       else if (prefix_set & ORISCUS)
540         glyph_name = "solesmes.oriscus";
541       else if (prefix_set & STROPHA)
542         if (prefix_set & AUCTUM)
543           glyph_name = "solesmes.stropha.aucta";
544         else glyph_name = "solesmes.stropha";
545       else if (prefix_set & INCLINATUM)
546         if (prefix_set & AUCTUM)
547           glyph_name = "solesmes.incl.auctum";
548         else if (prefix_set & DEMINUTUM)
549           glyph_name = "solesmes.incl.parvum";
550         else
551           glyph_name = "vaticana.inclinatum";
552       else if (prefix_set & DEMINUTUM)
553         if (i == 0)
554           {
555             // initio debilis
556             glyph_name = "vaticana.reverse.plica";
557           }
558         else if (prev_delta_pitch > 0)
559           {
560             // epiphonus
561             if (! (prev_context_info & FLEXA_RIGHT))
562               /* correct head of previous primitive */
563               if (prev_delta_pitch > 1)
564                 prev_glyph_name = "vaticana.epiphonus";
565               else
566                 prev_glyph_name = "vaticana.vepiphonus";
567             if (prev_delta_pitch > 1)
568               glyph_name = "vaticana.plica";
569             else
570               glyph_name = "vaticana.vplica";
571           }
572         else if (prev_delta_pitch < 0)
573           {
574             // cephalicus
575             if (! (prev_context_info & FLEXA_RIGHT))
576               /* correct head of previous primitive */
577               {
578                 if (i > 1)
579                   {
580                     /* cephalicus head with fixed size cauda */
581                     prev_glyph_name = "vaticana.inner.cephalicus";
582                   }
583                 else
584                   {
585                     /* cephalicus head without cauda */
586                     prev_glyph_name = "vaticana.cephalicus";
587                   }
588
589                 /*
590                  * Flexa has no variable size cauda if its left head is
591                  * stacked on the right head.  This is true for
592                  * cephalicus.  Hence, remove the cauda.
593                  *
594                  * Urgh: for the current implementation, this rule only
595                  * applies for cephalicus; but it is a fundamental rule.
596                  * Therefore, the following line of code should be
597                  * placed somewhere else.
598                  */
599                 prev_primitive->set_property ("add-cauda",
600                                               ly_bool2scm (false));
601               }
602             if (prev_delta_pitch < - 1)
603               glyph_name = "vaticana.reverse.plica";
604             else
605               glyph_name = "vaticana.reverse.vplica";
606           }
607         else // (prev_delta_pitch == 0)
608           {
609             primitive->programming_error ("Vaticana_ligature:"
610                                           " deminutum head must have different"
611                                           " pitch -> ignoring grob");
612           }
613       else if (prefix_set & (CAVUM | LINEA))
614         if ((prefix_set & CAVUM) && (prefix_set & LINEA))
615           glyph_name = "vaticana.linea.punctum.cavum";
616         else if (prefix_set & CAVUM)
617           glyph_name = "vaticana.punctum.cavum";
618         else
619           glyph_name = "vaticana.linea.punctum";
620       else if (prefix_set & AUCTUM)
621         if (prefix_set & ASCENDENS)
622           glyph_name = "solesmes.auct.asc";
623         else
624           glyph_name = "solesmes.auct.desc";
625       else if ((context_info & STACKED_HEAD)
626                && (context_info & PES_UPPER))
627         if (prev_delta_pitch > 1)
628           glyph_name = "vaticana.upes";
629         else
630           glyph_name = "vaticana.vupes";
631       else
632         glyph_name = "vaticana.punctum";
633
634       /*
635        * This head needs a cauda, if it starts a flexa, is not the upper
636        * head of a pes, and if it is a punctum.
637        */
638       if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
639         if (glyph_name == "vaticana.punctum")
640           primitive->set_property ("add-cauda", ly_bool2scm (true));
641
642       /*
643        * Execptional rule for porrectus:
644        *
645        * If the current head is preceded by a \flexa and succeded by a
646        * \pes (e.g. "a \flexa g \pes a"), then join the current head and
647        * the previous head into a single curved flexa shape.
648        */
649       if ((context_info & FLEXA_RIGHT) && (context_info & PES_LOWER))
650         {
651           check_for_prefix_loss (prev_primitive);
652           prev_glyph_name = "flexa";
653           prev_primitive->set_property ("flexa-height",
654                                         scm_from_int (prev_delta_pitch));
655           prev_primitive->set_property ("flexa-width",
656                                         scm_from_double (flexa_width));
657           bool add_cauda = !(prev_prefix_set && PES_OR_FLEXA);
658           prev_primitive->set_property ("add-cauda",
659                                         ly_bool2scm (add_cauda));
660           check_for_prefix_loss (primitive);
661           glyph_name = "";
662           primitive->set_property ("flexa-width",
663                                    scm_from_double (flexa_width));
664         }
665
666       /*
667        * Exceptional rule for pes:
668        *
669        * If this head is stacked on the previous one due to a \pes, then
670        * set the glyph of the previous head to that for this special
671        * case, thereby avoiding potential vertical collision with the
672        * current head.
673        */
674       if (prefix_set & PES_OR_FLEXA)
675         {
676           if ((context_info & PES_UPPER) && (context_info & STACKED_HEAD))
677             {
678               if (prev_glyph_name == "vaticana.punctum")
679                 if (prev_delta_pitch > 1)
680                   prev_glyph_name = "vaticana.lpes";
681                 else
682                   prev_glyph_name = "vaticana.vlpes";
683             }
684         }
685
686       if (prev_primitive)
687         prev_primitive->set_property ("glyph-name",
688                                       ly_string2scm (prev_glyph_name));
689
690       /*
691        * In the backend, flexa shapes and joins need to know about line
692        * thickness.  Hence, for simplicity, let's distribute the
693        * ligature grob's value for thickness to each ligature head (even
694        * if not all of them need to know).
695        */
696       primitive->set_property ("thickness", scm_from_double (thickness));
697
698       prev_primitive = primitive;
699       prev_prefix_set = prefix_set;
700       prev_context_info = context_info;
701       prev_delta_pitch = delta_pitch;
702       prev_glyph_name = glyph_name;
703     }
704
705   prev_primitive->set_property ("glyph-name",
706                                 ly_string2scm (prev_glyph_name));
707
708   align_heads (primitives, flexa_width, thickness);
709
710   // append all dots to paper column of ligature's last head
711   add_mora_column (prev_primitive->get_column ());
712
713 #if 0 // experimental code to collapse spacing after ligature
714   /* TODO: set to max (old/new spacing-increment), since other
715      voices/staves also may want to set this property. */
716   Item *first_primitive = dynamic_cast<Item *> (primitives[0].grob ());
717   Paper_column *paper_column = first_primitive->get_column ();
718   paper_column->warning (_f ("Vaticana_ligature_engraver: "
719                              "setting `spacing-increment = %f': ptr =%ul",
720                              ligature_width, paper_column));
721   paper_column->
722     set_property ("forced-spacing", scm_from_double (ligature_width));
723 #endif
724 }
725
726 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, rest);
727 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, note_head);
728 ADD_TRANSLATOR (Vaticana_ligature_engraver,
729                 /* doc */
730                 "Handle ligatures by glueing special ligature heads together.",
731
732                 /* create */
733                 "VaticanaLigature "
734                 "DotColumn ",
735
736                 /* read */
737                 "",
738
739                 /* write */
740                 ""
741                 );