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