w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

gregoriotex-write.c
Go to the documentation of this file.
1 /*
2  * Gregorio is a program that translates gabc files to GregorioTeX
3  * This file contains functions for writing GregorioTeX from Gregorio structures.
4  *
5  * Copyright (C) 2008-2021 The Gregorio Project (see CONTRIBUTORS.md)
6  *
7  * This file is part of Gregorio.
8  *
9  * This program is free software: you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the Free
11  * Software Foundation, either version 3 of the License, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program. If not, see <http://www.gnu.org/licenses/>. */
21 
22 /**
23  * @file
24  * @brief The plugin which writes a GregorioTeX score.
25  */
26 
27 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <time.h>
33 #include "bool.h"
34 #include "struct.h"
35 #include "unicode.h"
36 #include "messages.h"
37 #include "characters.h"
38 #include "plugins.h"
39 #include "support.h"
40 #include "utf8strings.h"
41 
42 #include "gregoriotex.h"
43 
44 #define BUFSIZE 128
45 
46 /* a structure containing the status */
47 typedef struct gregoriotex_status {
49 
50  /* true if the current_glyph will have an additional line under or not
51  * (useful to determine the length of the bar in case of a flexa starting
52  * at d */
54 
55  signed char top_height;
56  signed char bottom_height;
57 
58  /* indicates if there is a translation on the line */
60 
61  /* indicates if there is "above lines text" on the line */
63 
66 
67 #define UNDETERMINED_HEIGHT -127
68 
69 #define MAX_AMBITUS 5
70 static const char *tex_ambitus[] = {
71  "", "One", "Two", "Three", "Four", "Five"
72 };
73 
74 #define SHAPE(NAME) static const char *const SHAPE_##NAME = #NAME
75 SHAPE(Ancus);
76 SHAPE(AncusLongqueue);
77 SHAPE(AscendensOriscus);
78 SHAPE(AscendensOriscusLineBL);
79 SHAPE(AscendensOriscusLineTL);
80 SHAPE(AscendensOriscusScapus);
81 SHAPE(AscendensOriscusScapusLongqueue);
82 SHAPE(AscendensOriscusScapusOpenqueue);
83 SHAPE(AscendensPunctumInclinatum);
84 SHAPE(DescendensOriscus);
85 SHAPE(DescendensOriscusLineBL);
86 SHAPE(DescendensOriscusLineTL);
87 SHAPE(DescendensOriscusScapus);
88 SHAPE(DescendensOriscusScapusLongqueue);
89 SHAPE(DescendensOriscusScapusOpenqueue);
90 SHAPE(DescendensPunctumInclinatum);
93 SHAPE(Flexus);
94 SHAPE(FlexusLongqueue);
95 SHAPE(FlexusNobar);
96 SHAPE(FlexusOpenqueue);
97 SHAPE(FlexusOriscus);
98 SHAPE(FlexusOriscusInusitatus);
99 SHAPE(FlexusOriscusScapus);
100 SHAPE(FlexusOriscusScapusInusitatus);
101 SHAPE(FlexusOriscusScapusInusitatusLongqueue);
102 SHAPE(FlexusOriscusScapusInusitatusOpenqueue);
103 SHAPE(FlexusOriscusScapusLongqueue);
104 SHAPE(FlexusOriscusScapusOpenqueue);
105 SHAPE(Linea);
106 SHAPE(LineaPunctum);
109 SHAPE(OriscusDeminutus);
110 SHAPE(Pes);
111 SHAPE(PesAscendensOriscus);
112 SHAPE(PesDescendensOriscus);
113 SHAPE(PesQuadratum);
114 SHAPE(PesQuadratumLongqueue);
115 SHAPE(PesQuadratumOpenqueue);
116 SHAPE(PesQuassus);
117 SHAPE(PesQuassusInusitatus);
118 SHAPE(PesQuassusInusitatusLongqueue);
119 SHAPE(PesQuassusInusitatusOpenqueue);
120 SHAPE(PesQuassusLongqueue);
121 SHAPE(PesQuassusOpenqueue);
122 SHAPE(Porrectus);
123 SHAPE(PorrectusFlexus);
124 SHAPE(PorrectusFlexusLongqueue);
125 SHAPE(PorrectusFlexusNobar);
126 SHAPE(PorrectusLongqueue);
127 SHAPE(PorrectusNobar);
128 SHAPE(Punctum);
129 SHAPE(PunctumAscendens);
130 SHAPE(PunctumDeminutus);
131 SHAPE(PunctumDescendens);
132 SHAPE(PunctumInclinatumAuctus);
133 SHAPE(PunctumInclinatumDeminutus);
134 SHAPE(PunctumLineBL);
135 SHAPE(PunctumLineTL);
136 SHAPE(Quilisma);
137 SHAPE(QuilismaPes);
138 SHAPE(QuilismaPesQuadratum);
139 SHAPE(QuilismaPesQuadratumLongqueue);
140 SHAPE(QuilismaPesQuadratumOpenqueue);
141 SHAPE(StansPunctumInclinatum);
142 SHAPE(Salicus);
143 SHAPE(SalicusFlexus);
144 SHAPE(SalicusLongqueue);
145 SHAPE(Scandicus);
148 SHAPE(Stropha);
149 SHAPE(StrophaAucta);
150 SHAPE(StrophaAuctaLongtail);
151 SHAPE(Torculus);
152 SHAPE(TorculusLiquescens);
153 SHAPE(TorculusLiquescensQuilisma);
154 SHAPE(TorculusQuilisma);
155 SHAPE(TorculusResupinus);
156 SHAPE(TorculusResupinusQuilisma);
157 SHAPE(Virga);
158 SHAPE(VirgaLongqueue);
159 SHAPE(VirgaOpenqueue);
160 SHAPE(VirgaReversa);
161 SHAPE(VirgaReversaAscendens);
162 SHAPE(VirgaReversaDescendens);
163 SHAPE(VirgaReversaLongqueue);
164 SHAPE(VirgaReversaLongqueueAscendens);
165 SHAPE(VirgaReversaLongqueueDescendens);
166 SHAPE(VirgaReversaOpenqueue);
167 SHAPE(VirgaReversaOpenqueueAscendens);
168 SHAPE(VirgaReversaOpenqueueDescendens);
169 
170 #define LIQ(NAME) static const char *const LIQ_##NAME = #NAME
171 LIQ(Deminutus);
172 LIQ(Ascendens);
173 LIQ(Descendens);
175 LIQ(InitioDebilisDeminutus);
176 LIQ(InitioDebilisAscendens);
177 LIQ(InitioDebilisDescendens);
179 
180 #define FUSE(NAME) static const char *const FUSE_##NAME = #NAME
181 FUSE(Lower);
182 FUSE(LowerOblatus);
183 FUSE(Upper);
184 FUSE(UpperOblatus);
185 FUSE(Down);
186 FUSE(Up);
187 
188 /* the value indicating to GregorioTeX that there is no flat */
189 #define NO_KEY_FLAT LOWEST_PITCH
190 
191 static __inline signed char pitch_value(const signed char height) {
192  /* right now height == pitch, but this function allows us to change
193  * the offset easily */
194  return height;
195 }
196 
197 static __inline int bool_to_int(bool value) {
198  return value? 1 : 0;
199 }
200 
201 typedef enum queuetype {
207 
209  const gregorio_note *const queue_note, const signed char adjustment)
210 {
211  switch (note->u.note.pitch + adjustment - LOWEST_PITCH) {
212  case 0:
214  case 1:
215  return Q_ON_BOTTOM_LINE;
216  case 2:
217  return queue_note->low_ledger_line?
219  case 3:
220  return queue_note->low_ledger_line?
222  case 5:
223  case 7:
224  case 9:
225  case 11:
226  case 13:
228  default:
230  }
231 }
232 
233 static queuetype queuetype_of(const gregorio_note *const note) {
234  return adjusted_queuetype_of(note, note, 0);
235 }
236 
239 
240 /*
241  * The different liquescentiae are:
242  * 'Nothing'
243  * 'InitioDebilis'
244  * 'Deminutus
245  * 'Ascendens'
246  * 'Descendens'
247  * 'InitioDebilisDeminutus'
248  * 'InitioDebilisAscendens'
249  * 'InitioDebilisDescendens'
250  *
251  * They also are and must be the same as in squarize.py.
252  */
253 
256 {
257  gregorio_liquescentia liquescentia = glyph->u.notes.liquescentia;
258 
259  switch (type) {
260  case LG_ALL:
261  break;
262  case LG_NO_INITIO:
263  liquescentia &= ~~L_INITIO_DEBILIS;
264  break;
265  case LG_ONLY_DEMINUTUS:
266  liquescentia &= L_INITIO_DEBILIS | L_DEMINUTUS;
267  break;
268  case LG_FUSIBLE_INITIO:
269  if (glyph->u.notes.fuse_to_next_glyph) {
270  liquescentia &= L_INITIO_DEBILIS;
271  break;
272  }
273  /* fall through */
274  case LG_NONE:
275  liquescentia = L_NO_LIQUESCENTIA;
276  break;
277  }
278 
279  /* now we convert liquescentia into the good GregorioTeX liquescentia
280  * numbers */
281 
282  switch (liquescentia & ~L_FUSED) {
283  case L_DEMINUTUS:
284  return LIQ_Deminutus;
285  case L_AUCTUS_ASCENDENS:
286  return LIQ_Ascendens;
287  case L_AUCTUS_DESCENDENS:
288  return LIQ_Descendens;
289  case L_INITIO_DEBILIS:
290  return LIQ_InitioDebilis;
297  case L_NO_LIQUESCENTIA:
298  /* break out and return "Nothing" */
299  break;
300  }
301 
302  return LIQ_Nothing;
303 }
304 
305 static __inline int compute_ambitus(const gregorio_note *const current_note)
306 {
307  int first = current_note->u.note.pitch;
309  int ambitus;
310  if (first < second) {
311  ambitus = second - first;
312  } else {
313  ambitus = first - second;
314  }
315  gregorio_assert2(ambitus >= 1 && ambitus <= MAX_AMBITUS, compute_ambitus,
316  "unsupported ambitus: %d", ambitus, return 0);
317  return ambitus;
318 }
319 
320 static const char *compute_glyph_name(const gregorio_glyph *const glyph,
321  const char *shape, const gtex_glyph_liquescentia ltype,
322  bool is_single_note)
323 {
324  static char buf[BUFSIZE];
325 
326  const char *liquescentia = gregoriotex_determine_liquescentia(ltype, glyph);
328  int ambitus1, ambitus2, ambitus3, fuse_ambitus = 0;
329  const char *fuse_head = "", *fuse_tail = "";
331  int fuse_to_next_note, fuse_from_previous_note =
332  (previous && previous->type == GRE_GLYPH)
333  ? previous->u.notes.fuse_to_next_glyph : 0;
334 
335  /* then we start making our formula */
336  gregorio_assert(glyph, compute_glyph_name, "called with NULL pointer",
337  return "");
338  gregorio_assert(glyph->u.notes.first_note, compute_glyph_name,
339  "called with a glyph that have no note", return "");
340 
341  fuse_to_next_note = glyph->u.notes.fuse_to_next_glyph;
342 
343  switch (glyph->u.notes.glyph_type) {
344  case G_PODATUS:
345  case G_PUNCTUM:
346  case G_FLEXA:
347  /* directionally head-fusible */
348  if (fuse_from_previous_note < 0) {
349  if (glyph->u.notes.first_note->u.note.shape != S_QUILISMA
350  && glyph->u.notes.first_note->u.note.shape
352  if (fuse_from_previous_note < -1) {
353  fuse_head = FUSE_Lower;
354  } else if (glyph->u.notes.first_note->u.note.shape
356  || glyph->u.notes.first_note->u.note.shape
358  /* fuse_from_previous_note will be -1 here */
359  fuse_head = FUSE_LowerOblatus;
360  }
361  }
362  } else if (fuse_from_previous_note > 0) {
363  if (fuse_from_previous_note > 1) {
364  fuse_head = FUSE_Upper;
365  } else if (glyph->u.notes.first_note->u.note.shape
367  || glyph->u.notes.first_note->u.note.shape
369  /* fuse_from_previous_note will be 1 here */
370  fuse_head = FUSE_UpperOblatus;
371  }
372  }
373  break;
374 
375  default:
376  /* not directionally head-fusible */
377  break;
378  }
379 
380  switch (glyph->u.notes.glyph_type) {
381  case G_FLEXA:
382  if (fuse_to_next_note <= 0) {
383  /* a flexa is only fusible up */
384  break;
385  }
386  /* else fall through */
387  case G_VIRGA_REVERSA:
388  case G_PUNCTUM:
389  /* tail-fusible */
390  if (fuse_to_next_note < 0) {
391  fuse_tail = FUSE_Down;
392  fuse_ambitus = -fuse_to_next_note;
393  } else if (fuse_to_next_note > 0) {
394  fuse_tail = FUSE_Up;
395  fuse_ambitus = fuse_to_next_note;
396  }
397 
398  if (*fuse_tail && liquescentia == LIQ_Nothing) {
399  liquescentia = "";
400  }
401  break;
402 
403  default:
404  /* not tail-fusible */
405  break;
406  }
407 
408  if (is_fused(glyph->u.notes.liquescentia)) {
409  if (shape == SHAPE_AscendensOriscusScapus
412  shape = SHAPE_AscendensOriscus;
413  } else if (shape == SHAPE_DescendensOriscusScapus
416  shape = SHAPE_DescendensOriscus;
417  } else if (shape == SHAPE_FlexusOriscusScapus
420  shape = SHAPE_FlexusOriscus;
421  } else if (shape == SHAPE_FlexusOriscusScapusInusitatus
425  }
426  }
427 
428  current_note = glyph->u.notes.first_note;
429  if (is_single_note) {
430  if (liquescentia == LIQ_Nothing) {
431  liquescentia = "";
432  }
433  if (!(*fuse_tail) && fuse_head != FUSE_UpperOblatus
434  && fuse_head != FUSE_LowerOblatus) {
435  /* single note fused shapes have weird names */
436  if (fuse_head == FUSE_Upper) {
437  if (shape == SHAPE_Punctum) {
438  shape = SHAPE_PunctumLineBL;
439  } else if (shape == SHAPE_AscendensOriscus) {
441  } else if (shape == SHAPE_DescendensOriscus) {
443  }
444  } else if (fuse_head == FUSE_Lower) {
445  if (shape == SHAPE_Punctum) {
446  shape = SHAPE_PunctumLineTL;
447  } else if (shape == SHAPE_AscendensOriscus) {
449  } else if (shape == SHAPE_DescendensOriscus) {
451  }
452  }
453  fuse_head = "";
454  }
455  gregorio_snprintf(buf, BUFSIZE, "%s%s%s%s%s", fuse_head, shape,
456  tex_ambitus[fuse_ambitus], liquescentia, fuse_tail);
457  return buf;
458  }
460  "called with a multi-note glyph that has only one note", return "");
462  compute_glyph_name, "unexpected unison on multi-note glyph",
463  return "");
464  if (is_fused(glyph->u.notes.liquescentia)) {
465  if (shape == SHAPE_Flexus || shape == SHAPE_FlexusLongqueue
466  || shape == SHAPE_FlexusOpenqueue) {
467  if (fuse_to_next_note) {
468  fuse_head = "";
469  }
470  if (*fuse_head) {
471  shape = SHAPE_Flexus;
472  } else {
473  shape = SHAPE_FlexusNobar;
474  }
475  } else if (shape == SHAPE_Porrectus
476  || shape == SHAPE_PorrectusLongqueue) {
477  shape = SHAPE_PorrectusNobar;
478  }
479  } else {
480  if (fuse_to_next_note && (shape == SHAPE_FlexusLongqueue
481  || shape == SHAPE_FlexusOpenqueue)) {
482  /* a porrectus-like flexus has no longqueue variant */
483  shape = SHAPE_Flexus;
484  }
485  }
486  if (shape == SHAPE_SalicusLongqueue && liquescentia != LIQ_Nothing) {
487  /* the salicus queue is at the end of the glyph, and it doesn't exist
488  * for the liquescent forms */
489  shape = SHAPE_Salicus;
490  }
492  if (!current_note->next) {
493  gregorio_snprintf(buf, BUFSIZE, "%s%s%s%s%s%s", fuse_head, shape,
494  tex_ambitus[ambitus1], tex_ambitus[fuse_ambitus],
495  liquescentia, fuse_tail);
496  return buf;
497  }
499  compute_glyph_name, "unexpected unison on multi-note glyph",
500  return "");
502  if (!current_note->next) {
503  gregorio_snprintf(buf, BUFSIZE, "%s%s%s%s%s%s%s", fuse_head, shape,
504  tex_ambitus[ambitus1], tex_ambitus[ambitus2],
505  tex_ambitus[fuse_ambitus], liquescentia, fuse_tail);
506  return buf;
507  }
509  compute_glyph_name, "unexpected unison on multi-note glyph",
510  return "");
511  gregorio_snprintf(buf, BUFSIZE, "%s%s%s%s%s%s%s%s", fuse_head, shape,
512  tex_ambitus[ambitus1], tex_ambitus[ambitus2], tex_ambitus[ambitus3],
513  tex_ambitus[fuse_ambitus], liquescentia, fuse_tail);
514  return buf;
515 }
516 
517 static const char *fusible_queued_shape(const gregorio_note *const note,
518  const gregorio_glyph *const glyph,
519  const char *const base_shape, const char *const longqueue_shape,
520  const char *const openqueue_shape)
521 {
522  const char *name = NULL;
523  if (glyph->u.notes.fuse_to_next_glyph < 0) {
524  /* queue size depends on the following note if fused down */
525  bool ambitus_one = (glyph->u.notes.fuse_to_next_glyph == -1);
526  switch (adjusted_queuetype_of(note, note,
527  glyph->u.notes.fuse_to_next_glyph)) {
529  if (ambitus_one) {
530  name = openqueue_shape;
531  break;
532  }
533  /* else fall through */
535  /* at ambitus one, long and short are swapped becuase the queue where
536  * the second note is on a space is longer than on a line */
537  name = ambitus_one? longqueue_shape : base_shape;
538  break;
539  case Q_ON_BOTTOM_LINE:
540  if (ambitus_one) {
541  name = openqueue_shape;
542  break;
543  }
544  /* else fall through */
546  /* at ambitus one, long and short are swapped becuase the queue where
547  * the second note is on a line is shorter than on a space */
548  name = ambitus_one? base_shape : longqueue_shape;
549  break;
550  }
551  } else if (glyph->u.notes.fuse_to_next_glyph) {
552  /* TODO (5.0?) handle queue size on upwards fusion */
553  switch (queuetype_of(note)) {
556  case Q_ON_BOTTOM_LINE:
557  name = base_shape;
558  break;
560  name = longqueue_shape;
561  break;
562  }
563  } else {
564  switch (queuetype_of(note)) {
566  name = base_shape;
567  break;
569  case Q_ON_BOTTOM_LINE:
570  name = openqueue_shape;
571  break;
573  name = longqueue_shape;
574  break;
575  }
576  }
577  gregorio_not_null(name, fusible_queued_shape, return base_shape);
578  return compute_glyph_name(glyph, name, LG_NONE, true);
579 }
580 
581 static __inline char *code_point(const char *const shape, const bool is_cavum,
582  char *const buf, const size_t bufsize)
583 {
584  if (is_cavum) {
585  gregorio_snprintf(buf, bufsize, "\\GreCavum{%s}", shape);
586  } else {
587  gregorio_snprintf(buf, bufsize, "\\GreCP%s", shape);
588  }
589  return buf;
590 }
591 
592 static const char *determine_note_glyph_name(const gregorio_note *const note,
594 {
595  static char buf[128], cpbuf[96];
596  const char *name = "";
597 
598  gregorio_assert(note, determine_note_glyph_name, "called with NULL pointer",
599  return "");
600 
601  *type = AT_ONE_NOTE;
602  switch (note->u.note.shape) {
616  case S_PUNCTUM:
619  return SHAPE_PunctumAscendens;
622  case S_PUNCTUM_DEMINUTUS:
623  return SHAPE_PunctumDeminutus;
624  case S_LINEA:
625  return SHAPE_Linea;
626  case S_LINEA_PUNCTUM:
627  return SHAPE_LineaPunctum;
628  case S_VIRGA:
629  switch (queuetype_of(note)) {
631  return SHAPE_Virga;
633  case Q_ON_BOTTOM_LINE:
634  return SHAPE_VirgaOpenqueue;
636  return SHAPE_VirgaLongqueue;
637  } /* all cases return, so this line is not hit; LCOV_EXCL_LINE */
638  /* LCOV_EXCL_START */
639  gregorio_fail2(determine_note_glyph_name, "unknown queuetype: %d",
640  queuetype_of(note));
641  return "";
642  /* LCOV_EXCL_STOP */
643  case S_VIRGA_REVERSA:
644  switch (note->u.note.liquescentia) {
645  case L_AUCTUS_ASCENDENS:
646  switch (queuetype_of(note)) {
650  break;
651  case Q_ON_BOTTOM_LINE:
653  break;
656  break;
657  }
658  if (note->u.note.pitch - LOWEST_PITCH == 3) {
659  /* if we're on the 'd' line, the queue could be long or short */
660  gregorio_snprintf(buf, sizeof buf,
661  "VirgaReversaAscendensOnDLine{%s}", code_point(name,
662  glyph->u.notes.is_cavum, cpbuf, sizeof cpbuf));
663  return buf;
664  }
665  return name;
666  case L_AUCTUS_DESCENDENS:
667  switch (queuetype_of(note)) {
671  case Q_ON_BOTTOM_LINE:
675  } /* all cases return, so this line is not hit; LCOV_EXCL_LINE */
676  /* LCOV_EXCL_START */
677  gregorio_fail2(determine_note_glyph_name, "unknown queuetype: %d",
678  queuetype_of(note));
679  return "";
680  /* LCOV_EXCL_STOP */
681  default:
684  }
685  case S_ORISCUS_ASCENDENS:
686  *type = AT_ORISCUS;
689  *type = AT_ORISCUS;
691  case S_ORISCUS_DEMINUTUS:
692  *type = AT_ORISCUS;
693  return SHAPE_OriscusDeminutus;
694  case S_QUILISMA:
695  *type = AT_QUILISMA;
705  case S_STROPHA:
706  *type = AT_STROPHA;
707  if (!(note->u.note.liquescentia &
709  return SHAPE_Stropha;
710  }
711  /* fall through */
712  case S_STROPHA_AUCTA:
713  *type = AT_STROPHA;
714  switch (queuetype_of(note)) {
717  return SHAPE_StrophaAucta;
718  case Q_ON_BOTTOM_LINE:
721  } /* all cases return, so this line is not hit; LCOV_EXCL_LINE */
722  /* LCOV_EXCL_START */
723  gregorio_fail2(determine_note_glyph_name, "unknown queuetype: %d",
724  queuetype_of(note));
725  return "";
726  /* LCOV_EXCL_STOP */
727  case S_FLAT:
728  return SHAPE_Flat;
729  case S_FLAT_PAREN:
730  return SHAPE_FlatParen;
731  case S_SHARP:
732  return SHAPE_Sharp;
733  case S_SHARP_PAREN:
734  return SHAPE_SharpParen;
735  case S_NATURAL:
736  return SHAPE_Natural;
737  case S_NATURAL_PAREN:
738  return SHAPE_NaturalParen;
739  default:
740  /* not reachable unless there's a programming error */
741  /* LCOV_EXCL_START */
743  "called with unknown shape: %s",
745  return "";
746  /* LCOV_EXCL_STOP */
747  }
748 }
749 
750 static __inline gregorio_note *first_note_of(
751  const gregorio_glyph *const glyph) {
752  assert(glyph->type == GRE_GLYPH);
753  assert(glyph->u.notes.first_note);
754  assert(glyph->u.notes.first_note->type == GRE_NOTE);
755  return glyph->u.notes.first_note;
756 }
757 
758 static __inline signed char first_pitch_of(const gregorio_glyph *const glyph) {
759  return first_note_of(glyph)->u.note.pitch;
760 }
761 
762 static __inline gregorio_note *second_note_of(
763  const gregorio_glyph *const glyph) {
764  assert(glyph->type == GRE_GLYPH);
765  assert(glyph->u.notes.first_note);
766  assert(glyph->u.notes.first_note->type == GRE_NOTE);
767  assert(glyph->u.notes.first_note->next);
768  assert(glyph->u.notes.first_note->next->type == GRE_NOTE);
769  return glyph->u.notes.first_note->next;
770 }
771 
772 static __inline signed char second_pitch_of(const gregorio_glyph *const glyph) {
773  return second_note_of(glyph)->u.note.pitch;
774 }
775 
776 static __inline gregorio_note *third_note_of(
777  const gregorio_glyph *const glyph) {
778  assert(glyph->type == GRE_GLYPH);
779  assert(glyph->u.notes.first_note);
780  assert(glyph->u.notes.first_note->type == GRE_NOTE);
781  assert(glyph->u.notes.first_note->next);
782  assert(glyph->u.notes.first_note->next->type == GRE_NOTE);
783  assert(glyph->u.notes.first_note->next->next);
784  assert(glyph->u.notes.first_note->next->next->type == GRE_NOTE);
785  return glyph->u.notes.first_note->next->next;
786 }
787 
788 static __inline signed char third_pitch_of(const gregorio_glyph *const glyph) {
789  return third_note_of(glyph)->u.note.pitch;
790 }
791 
792 static __inline const char *porrectus_shape(const gregorio_glyph *const glyph,
793  const char *base_shape, const char *longqueue_shape) {
794  const gregorio_note *const first_note = first_note_of(glyph);
795  if ((glyph->u.notes.glyph_type == G_PORRECTUS
796  && (glyph->u.notes.liquescentia & L_DEMINUTUS))
797  || first_note->u.note.pitch - second_pitch_of(glyph) == 1) {
798  switch (queuetype_of(first_note)) {
801  case Q_ON_BOTTOM_LINE:
802  return base_shape;
804  return longqueue_shape;
805  }
806  } /* must be optimized out; LCOV_EXCL_LINE */
807  return base_shape;
808 }
809 
810 static __inline const char *flexus_shape(const gregorio_glyph *const glyph,
811  const signed char ambitus, const char *base_shape,
812  const char *longqueue_shape, const char *openqueue_shape) {
813  const bool ambitus_one = (ambitus == 1);
815  0)) {
817  if (ambitus_one) {
818  return openqueue_shape;
819  }
820  /* else fall through */
822  /* at ambitus one, long and short are swapped becuase the queue where
823  * the second note is on a space is longer than on a line */
824  return ambitus_one? longqueue_shape : base_shape;
825  case Q_ON_BOTTOM_LINE:
826  if (ambitus_one) {
827  return openqueue_shape;
828  }
829  /* else fall through */
831  /* at ambitus one, long and short are swapped becuase the queue where
832  * the second note is on a line is shorter than on a space */
833  return ambitus_one? base_shape : longqueue_shape;
834  }
835  /* not reachable unless there's a programming error */
836  /* LCOV_EXCL_START */
837  gregorio_fail(flexus_shape, "unexpected queue length");
838  return base_shape;
839  /* LCOV_EXCL_STOP */
840 }
841 
842 static __inline const char *quadratum_shape(const gregorio_glyph *const glyph,
843  const char *base_shape, const char *longqueue_shape,
844  const char *openqueue_shape) {
845  const bool ambitus_one =
847  if (!is_tail_liquescentia(glyph->u.notes.liquescentia)) {
849  second_note_of(glyph), 0)) {
851  if (ambitus_one) {
852  return openqueue_shape;
853  }
854  /* else fall through */
856  /* at ambitus one, long and short are swapped becuase the queue
857  * where the first note is on a space is longer than on a line */
858  return ambitus_one? longqueue_shape : base_shape;
859  case Q_ON_BOTTOM_LINE:
860  if (ambitus_one) {
861  return openqueue_shape;
862  }
863  /* else fall through */
865  /* at ambitus one, long and short are swapped becuase the queue
866  * where the first note is on a line is shorter than on a space */
867  return ambitus_one? base_shape : longqueue_shape;
868  }
869  /* not reachable unless there's a programming error */
870  /* LCOV_EXCL_START */
871  gregorio_fail(quadratum_shape, "unexpected queue length");
872  /* fall out of the conditional */
873  }
874  /* LCOV_EXCL_STOP */
875  return base_shape;
876 }
877 
878 /* the function that calculates the number of the glyph. It also
879  * calculates the type, used for determining the position of signs. Type is
880  * very basic, it is only the global dimensions : torculus, one_note, etc. */
881 
883  gtex_alignment *const type, gtex_type *const gtype)
884 {
885  const char *shape = NULL;
887  signed char ambitus;
888  const gregorio_note *second_note;
890  "called with NULL pointer", return "");
892  "called with a glyph that has no note", return "");
893  *gtype = T_ONE_NOTE;
894  switch (glyph->u.notes.glyph_type) {
895  case G_PODATUS:
896  switch (glyph->u.notes.first_note->u.note.shape) {
897  case S_QUILISMA:
898  *type = AT_QUILISMA;
899  /* the next if is because we made the choice that AUCTUS shapes
900  * look like pes quadratum. */
901  if (glyph->u.notes.liquescentia
903  *gtype = T_QUILISMA_PES_QUADRATUM;
905  } else {
906  *gtype = T_QUILISMA_PES;
907  shape = SHAPE_QuilismaPes;
908  }
909  ltype = LG_NO_INITIO;
910  break;
911  case S_ORISCUS_ASCENDENS:
913  *type = AT_ORISCUS;
914  *gtype = T_PES_QUASSUS;
917  ltype = LG_NO_INITIO;
918  break;
921  *type = AT_ORISCUS;
922  *gtype = T_PES_QUASSUS;
926  ltype = LG_NO_INITIO;
927  break;
928  default:
929  *type = AT_ONE_NOTE;
930  if (glyph->u.notes.liquescentia
932  *gtype = T_PES_QUADRATUM;
933  shape = SHAPE_PesQuadratum;
934  } else {
935  *gtype = T_PES;
936  shape = SHAPE_Pes;
937  }
938  ltype = LG_ALL;
939  break;
940  }
941  break;
942  case G_PES_QUADRATUM:
943  switch (glyph->u.notes.first_note->u.note.shape) {
944  case S_QUILISMA:
945  *type = AT_QUILISMA;
946  *gtype = T_QUILISMA_PES_QUADRATUM;
950  ltype = LG_NO_INITIO;
951  break;
952  default:
953  *type = AT_ONE_NOTE;
954  *gtype = T_PES_QUADRATUM;
957  ltype = LG_ALL;
958  break;
959  }
960  break;
962  *type = AT_ONE_NOTE;
963  *gtype = T_PES_ORISCUS;
965  ltype = LG_ALL;
966  break;
968  *type = AT_ONE_NOTE;
969  *gtype = T_PES_ORISCUS;
971  ltype = LG_ALL;
972  break;
973  case G_FLEXA:
975  if (glyph->u.notes.liquescentia & L_DEMINUTUS) {
977  } else {
978  if (ambitus == 1) {
979  *type = AT_FLEXUS_1;
980  } else {
981  *type = AT_FLEXUS;
982  }
983  }
984  switch (glyph->u.notes.first_note->u.note.shape) {
985  case S_ORISCUS_ASCENDENS:
986  *gtype = T_FLEXUS_ORISCUS;
988  ltype = LG_NO_INITIO;
989  break;
990 
992  *gtype = T_FLEXUS_ORISCUS;
993  shape = SHAPE_FlexusOriscus;
994  ltype = LG_NO_INITIO;
995  break;
996 
998  *gtype = T_FLEXUS_ORISCUS_SCAPUS;
999  shape = flexus_shape(glyph, ambitus,
1003  ltype = LG_NO_INITIO;
1004  break;
1005 
1007  *gtype = T_FLEXUS_ORISCUS_SCAPUS;
1008  shape = flexus_shape(glyph, ambitus, SHAPE_FlexusOriscusScapus,
1011  ltype = LG_NO_INITIO;
1012  break;
1013 
1014  default:
1015  *gtype = glyph->u.notes.fuse_to_next_glyph? T_PORRECTUS : T_FLEXUS;
1016  shape = flexus_shape(glyph, ambitus, SHAPE_Flexus,
1018  ltype = LG_NO_INITIO;
1019  break;
1020  }
1021  break;
1022  case G_TORCULUS:
1023  *gtype = T_TORCULUS;
1024  if (glyph->u.notes.first_note->u.note.shape == S_QUILISMA) {
1025  *type = AT_QUILISMA;
1026  shape = SHAPE_TorculusQuilisma;
1027  ltype = LG_NO_INITIO;
1028  } else {
1029  *type = AT_ONE_NOTE;
1030  shape = SHAPE_Torculus;
1031  ltype = LG_ALL;
1032  }
1033  break;
1034  case G_TORCULUS_LIQUESCENS:
1035  *gtype = T_TORCULUS_LIQUESCENS;
1036  if (glyph->u.notes.first_note->u.note.shape == S_QUILISMA) {
1037  *type = AT_QUILISMA;
1039  } else {
1040  *type = AT_ONE_NOTE;
1041  shape = SHAPE_TorculusLiquescens;
1042  }
1043  ltype = LG_ONLY_DEMINUTUS;
1044  break;
1046  *type = AT_ONE_NOTE;
1047  *gtype = T_TORCULUS_RESUPINUS_FLEXUS;
1048  break;
1049  case G_PORRECTUS:
1050  *type = AT_PORRECTUS;
1051  *gtype = T_PORRECTUS;
1054  ltype = LG_NO_INITIO;
1055  break;
1056  case G_TORCULUS_RESUPINUS:
1057  *gtype = T_TORCULUS_RESUPINUS;
1058  if (glyph->u.notes.first_note->u.note.shape == S_QUILISMA) {
1059  *type = AT_QUILISMA;
1061  } else {
1062  *type = AT_ONE_NOTE;
1063  shape = SHAPE_TorculusResupinus;
1064  }
1065  ltype = LG_ALL;
1066  break;
1067  case G_PORRECTUS_FLEXUS:
1068  *type = AT_PORRECTUS;
1069  *gtype = T_PORRECTUS_FLEXUS;
1072  ltype = LG_NO_INITIO;
1073  break;
1074  case G_PORRECTUS_NO_BAR:
1075  *type = AT_PORRECTUS;
1076  *gtype = T_TORCULUS_RESUPINUS;
1077  shape = SHAPE_PorrectusNobar;
1078  ltype = LG_NO_INITIO;
1079  break;
1081  *type = AT_PORRECTUS;
1082  *gtype = T_TORCULUS_RESUPINUS_FLEXUS;
1084  ltype = LG_NO_INITIO;
1085  break;
1086  case G_ANCUS:
1087  *type = AT_ONE_NOTE;
1088  gregorio_assert(glyph->u.notes.liquescentia & L_DEMINUTUS,
1090  "encountered non-deminutus ancus", break);
1091  second_note = second_note_of(glyph);
1092  if (first_pitch_of(glyph) - second_note->u.note.pitch == 1) {
1093  *type = AT_FLEXUS_1;
1094  } else {
1095  *type = AT_FLEXUS;
1096  }
1097  *gtype = T_ANCUS;
1098  ambitus = first_pitch_of(glyph) - second_pitch_of(glyph);
1099  shape = flexus_shape(glyph, ambitus, SHAPE_Ancus,
1101  ltype = LG_ONLY_DEMINUTUS;
1102  break;
1103  case G_SCANDICUS:
1104  *type = AT_ONE_NOTE;
1105  *gtype = T_SCANDICUS;
1106  shape = SHAPE_Scandicus;
1107  ltype = LG_NO_INITIO;
1108  break;
1109  case G_SALICUS:
1110  *type = AT_ONE_NOTE;
1111  *gtype = T_SALICUS;
1112  ambitus = third_pitch_of(glyph) - second_pitch_of(glyph);
1113  shape = flexus_shape(glyph, ambitus, SHAPE_Salicus,
1115  ltype = LG_NO_INITIO;
1116  break;
1117  case G_SALICUS_FLEXUS:
1118  *type = AT_ONE_NOTE;
1119  *gtype = T_SALICUS_FLEXUS;
1120  shape = SHAPE_SalicusFlexus;
1121  ltype = LG_NO_INITIO;
1122  break;
1123  case G_PUNCTUM_INCLINATUM:
1124  case G_PUNCTA_INCLINATA:
1133  case G_PUNCTUM:
1134  case G_STROPHA:
1135  case G_VIRGA:
1136  case G_VIRGA_REVERSA:
1137  case G_STROPHA_AUCTA:
1138  case G_DISTROPHA:
1139  case G_DISTROPHA_AUCTA:
1140  case G_TRISTROPHA:
1141  case G_TRISTROPHA_AUCTA:
1142  case G_BIVIRGA:
1143  case G_TRIVIRGA:
1144  case G_ALTERATION:
1145  *type = AT_ONE_NOTE;
1146  break;
1147  default:
1148  /* not reachable unless there's a programming error */
1149  /* LCOV_EXCL_START */
1151  "called with unknown glyph: %s",
1152  gregorio_glyph_type_to_string(glyph->u.notes.glyph_type));
1153  break;
1154  /* LCOV_EXCL_STOP */
1155  }
1156  if (shape) {
1157  shape = compute_glyph_name(glyph, shape, ltype, false);
1158  }
1159  /* we fix *type with initio_debilis */
1160  if (*type == AT_ONE_NOTE) {
1161  if (is_initio_debilis(glyph->u.notes.liquescentia)) {
1163  }
1164  }
1165 
1166  return shape;
1167 }
1168 
1169 /**
1170  * This now does nothing useful, but we keep it here in case it may
1171  * be needed in future.
1172  */
1174 {
1176  "file or voice_info passed as NULL", return);
1177 }
1178 
1179 /* this function indicates if the syllable is the last of the line or score. */
1181 {
1183  if (!(syllable->next_syllable) || ((syllable->next_syllable->elements)[0]
1184  && (syllable->next_syllable->elements)[0]->type
1185  == GRE_END_OF_LINE)) {
1186  /* the next syllable start by an end of line */
1187  return true;
1188  }
1189  if (syllable->elements) {
1190  current_element = (syllable->elements)[0];
1191  while (current_element) {
1193  /* we return true only if the end of line is the last element */
1194  if (!(current_element->next)) {
1195  return true;
1196  } else {
1197  return false;
1198  }
1199  } else {
1201  }
1202  }
1203  }
1204  return false;
1205 }
1206 
1207 /*
1208  * ! @brief Prints the beginning of each text style
1209  */
1211 {
1213  return;
1214  }
1215  switch (style) {
1216  case ST_ITALIC:
1217  fprintf(f, "\\GreItalic{");
1218  break;
1219  case ST_SMALL_CAPS:
1220  fprintf(f, "\\GreSmallCaps{");
1221  break;
1222  case ST_BOLD:
1223  fprintf(f, "\\GreBold{");
1224  break;
1225  case ST_FORCED_CENTER:
1226  case ST_CENTER:
1227  fprintf(f, "}{");
1228  break;
1229  case ST_TT:
1230  fprintf(f, "\\GreTypewriter{");
1231  break;
1232  case ST_UNDERLINED:
1233  fprintf(f, "\\GreUnderline{");
1234  break;
1235  case ST_COLORED:
1236  fprintf(f, "\\GreColored{");
1237  break;
1238  case ST_ELISION:
1239  fprintf(f, "\\GreElision{");
1240  break;
1241  case ST_FIRST_WORD:
1242  fprintf(f, "\\GreFirstWord{");
1243  break;
1244  case ST_FIRST_SYLLABLE:
1245  fprintf(f, "\\GreFirstSyllable{");
1246  break;
1248  fprintf(f, "\\GreFirstSyllableInitial{");
1249  break;
1250  case ST_PROTRUSION_FACTOR:
1251  fprintf(f, "\\GreProtrusion{");
1252  break;
1253  case ST_PROTRUSION:
1254  fprintf(f, "{");
1255  break;
1256  default:
1257  break;
1258  }
1259 }
1260 
1261 /**
1262  * @brief Ends each text style
1263  */
1265 {
1267  return;
1268  }
1269  switch (style) {
1270  case ST_FORCED_CENTER:
1271  case ST_CENTER:
1272  case ST_SYLLABLE_INITIAL:
1273  fprintf(f, "}{");
1274  break;
1275  case ST_INITIAL:
1276  break;
1277  default:
1278  fprintf(f, "}");
1279  break;
1280  }
1281 }
1282 
1283 static __inline void tex_escape_text(FILE *const f, const char *text)
1284 {
1285  /* We escape these characters into \string\ddd (where ddd is the decimal
1286  * ASCII value of the character) for most escapes, and into \string\n for
1287  * newlines. We do it this way to get the "raw" string values through TeX
1288  * and into Lua, where the sequences become \ddd and \n respectively and
1289  * are translated into their byte values. Lua can then decide whether the
1290  * full strings should be evaluated by TeX as TeX or as strings */
1291  for (; *text; ++text) {
1292  switch (*text) {
1293  case '\\':
1294  case '{':
1295  case '}':
1296  case '~':
1297  case '%': /* currently, we'll never get %, but handle it anyway */
1298  case '#':
1299  case '"':
1300  /* these characters have special meaning to TeX */
1301  fprintf(f, "\\string\\%03d", *text);
1302  break;
1303  case '\n':
1304  fprintf(f, "\\string\\n");
1305  break;
1306  case '\r':
1307  /* ignore */
1308  break;
1309  default:
1310  /* UTF-8 multibyte sequences will fall into here, which is fine */
1311  fputc(*text, f);
1312  break;
1313  }
1314  }
1315 }
1316 
1317 static __inline void tex_escape_wtext(FILE *const f, const grewchar *text)
1318 {
1319  /* We escape these characters into \string\ddd (where ddd is the decimal
1320  * ASCII value of the character) for most escapes, and into \string\n for
1321  * newlines. We do it this way to get the "raw" string values through TeX
1322  * and into Lua, where the sequences become \ddd and \n respectively and
1323  * are translated into their byte values. Lua can then decide whether the
1324  * full strings should be evaluated by TeX as TeX or as strings */
1325  for (; *text; ++text) {
1326  switch (*text) {
1327  case L'\\':
1328  case L'{':
1329  case L'}':
1330  case L'~':
1331  case L'%': /* currently, we'll never get %, but handle it anyway */
1332  case L'#':
1333  case L'"':
1334  /* these characters have special meaning to TeX */
1335  fprintf(f, "\\string\\%03d", *text);
1336  break;
1337  /* There is currently no way to get a carriage return or a newline into
1338  * an <sp>, but we'll leave this code here, but #if'd out, in case we
1339  * need this function in a different context */
1340 #if 0
1341  case L'\n':
1342  fprintf(f, "\\string\\n");
1343  break;
1344  case L'\r':
1345  / * ignore * /
1346  break;
1347 #endif
1348  default:
1350  break;
1351  }
1352  }
1353 }
1354 
1355 /*
1356  * ! @brief Writes GregorioTeX special characters. This function takes the
1357  * special characters as input (i.e. from gabc representation), and writes them
1358  * * in GregorioTeX form.
1359  */
1361 {
1362  fprintf(f, "\\GreSpecial{");
1364  fprintf(f, "}");
1365 }
1366 
1368 {
1370 }
1371 
1372 static void gtex_print_char(FILE *f, const grewchar to_print)
1373 {
1374  switch (to_print) {
1375  case L'*':
1376  fprintf(f, "\\GreStar{}");
1377  break;
1378  case L'%':
1379  /* there's currently no way to get a % into gabc, so this wont be hit,
1380  * but we leave it here for safety and possible future use */
1381  /* LCOV_EXCL_START */
1382  fprintf(f, "\\%%{}");
1383  break;
1384  /* LCOV_EXCL_STOP */
1385  case L'\\':
1386  fprintf(f, "\\textbackslash{}");
1387  break;
1388  case L'&':
1389  fprintf(f, "\\&{}");
1390  break;
1391  case L'#':
1392  fprintf(f, "\\#{}");
1393  break;
1394  case L'+':
1395  fprintf(f, "\\GreDagger{}");
1396  break;
1397  case L'_':
1398  fprintf(f, "\\_{}");
1399  break;
1400  case L'-':
1401  fprintf(f, "\\GreHyph{}");
1402  break;
1403  default:
1404  gregorio_print_unichar(f, to_print);
1405  break;
1406  }
1407  return;
1408 }
1409 
1410 /* a function to map the internal ST_* styles to gregoriotex styles as defined
1411  * in gregoriotex-syllables.tex */
1413  const grestyle_style style)
1414 {
1415  switch (style) {
1416  case ST_ITALIC:
1417  return 1;
1418  case ST_BOLD:
1419  return 2;
1420  case ST_SMALL_CAPS:
1421  return 3;
1422  case ST_TT:
1423  return 4;
1424  case ST_UNDERLINED:
1425  return 5;
1426  case ST_COLORED:
1427  return 6;
1428  default:
1429  /* not reachable unless there's a programming error */
1430  /* LCOV_EXCL_START */
1432  "unrecognized style: %s", grestyle_style_to_string(style));
1433  return 0;
1434  /* LCOV_EXCL_STOP */
1435  }
1436 }
1437 
1438 /*
1439  * A quite hacky function: when we have only one style (typically italic) and
1440  * when this style is on all the parts, then we return this style.
1441  *
1442  */
1444  const gregorio_character *current_char)
1445 {
1446  grestyle_style possible_fixed_style = ST_NO_STYLE;
1447  grestyle_style in_fixed_style = ST_NO_STYLE;
1448  while (current_char) {
1449  if (!in_fixed_style) {
1450  /* before the first style */
1451  if (current_char->is_character) {
1452  /* got some character, so no future style can apply to the
1453  * entire syllable */
1454  return ST_NO_STYLE;
1455  }
1456  if (current_char->cos.s.type == ST_T_BEGIN) {
1457  switch (current_char->cos.s.style) {
1458  case ST_VERBATIM:
1459  case ST_SPECIAL_CHAR:
1460  /* these are pseudo-characters, and if they appear
1461  * before the first style, then the style does not
1462  * apply to the entire syllable */
1463  return ST_NO_STYLE;
1464 
1465  case ST_ITALIC:
1466  case ST_BOLD:
1467  case ST_TT:
1468  case ST_SMALL_CAPS:
1469  case ST_UNDERLINED:
1470  case ST_COLORED:
1471  if (possible_fixed_style) {
1472  if (current_char->cos.s.style != possible_fixed_style) {
1473  /* found a differing style */
1474  return ST_NO_STYLE;
1475  } else {
1476  /* same style which may apply to the entire
1477  * syllable */
1478  in_fixed_style = possible_fixed_style;
1479  }
1480  } else {
1481  /* we found a candidate fixed style */
1482  in_fixed_style = possible_fixed_style =
1483  current_char->cos.s.style;
1484  }
1485  break;
1486 
1487  default:
1488  /* anything else is "transparent" before the first real
1489  * or pseudo-character */
1490  break;
1491  }
1492  }
1493  /* else it's an end of a style, which we treat as "transparent"
1494  * noise */
1495  } else {
1496  /* we have a possible style */
1497  if (!current_char->is_character) {
1498  if (current_char->cos.s.type == ST_T_BEGIN) {
1499  switch (current_char->cos.s.style) {
1500  case ST_ITALIC:
1501  case ST_BOLD:
1502  case ST_TT:
1503  case ST_SMALL_CAPS:
1504  case ST_UNDERLINED:
1505  case ST_COLORED:
1506  if (current_char->cos.s.style != possible_fixed_style) {
1507  /* found a differing style */
1508  return ST_NO_STYLE;
1509  }
1510  /* else it's a (nested) open of the same style, which
1511  * doesn't change the style; however, nested styles
1512  * (should) have been eliminated by now */
1513  /* LCOV_EXCL_START */
1514  gregorio_fail(gregoriotex_fix_style, "encountered a "
1515  "nested style which should have been removed "
1516  "by now");
1517  break;
1518  /* LCOV_EXCL_STOP */
1519 
1520  default:
1521  /* anything else is a pseudo-character or a
1522  * "transparent" style, which don't affect the fixed
1523  * style */
1524  break;
1525  }
1526  } else if (current_char->cos.s.type == ST_T_END) {
1527  if (current_char->cos.s.style == possible_fixed_style) {
1528  /* we closed the possible fixed style; we don't return
1529  * yet to give it the chance to re-open */
1530  in_fixed_style = ST_NO_STYLE;
1531  }
1532  /* else it's a close of something we don't care about or a
1533  * style that wasn't opened, so for the purposes of this
1534  * function, these are "transparent" and don't affect the
1535  * fixed style */
1536  }
1537  }
1538  /* else it's a character, and that doesn't affect the fixed style */
1539  }
1540  current_char = current_char->next_character;
1541  }
1542  return possible_fixed_style;
1543 }
1544 
1545 /*
1546  * @brief Writes the translation.
1547  *
1548  * There is no special handling of translation text; that is, we just print the
1549  * entire string of text under the normal text line, without considering any
1550  * special centering or linebreaks.
1551  */
1552 static __inline void write_translation(FILE *f,
1553  gregorio_character *translation)
1554 {
1555  gregorio_not_null(translation, write_translation, return);
1559 }
1560 
1561 /* a function to compute the height of the flat of a key
1562  * the flat is always on the line of the */
1563 
1564 static char clef_flat_height(gregorio_clef clef, signed char line, bool flatted)
1565 {
1566  char offset = 6;
1567 
1568  if (!flatted) {
1569  return NO_KEY_FLAT;
1570  }
1571 
1572  switch (clef) {
1573  case CLEF_C:
1574  switch (line) {
1575  case 1:
1576  offset = 2;
1577  break;
1578  case 2:
1579  offset = 4;
1580  break;
1581  case 3:
1582  offset = 6;
1583  break;
1584  case 4:
1585  offset = 8;
1586  break;
1587  case 5:
1588  offset = 10;
1589  break;
1590  default:
1591  /* not reachable unless there's a programming error */
1592  /* LCOV_EXCL_START */
1593  gregorio_fail2(clef_flat_height, "unknown line number: %d", line);
1594  break;
1595  /* LCOV_EXCL_STOP */
1596  }
1597  break;
1598  case CLEF_F:
1599  switch (line) {
1600  case 1:
1601  offset = 6;
1602  break;
1603  case 2:
1604  offset = 8;
1605  break;
1606  case 3:
1607  offset = 3;
1608  break;
1609  case 4:
1610  offset = 5;
1611  break;
1612  case 5:
1613  offset = 7;
1614  break;
1615  default:
1616  /* not reachable unless there's a programming error */
1617  /* LCOV_EXCL_START */
1618  gregorio_fail2(clef_flat_height, "unknown line number: %d", line);
1619  break;
1620  /* LCOV_EXCL_STOP */
1621  }
1622  break;
1623  default:
1624  /* not reachable unless there's a programming error */
1625  /* LCOV_EXCL_START */
1626  gregorio_fail2(clef_flat_height, "unknown clef type: %d", clef);
1627  break;
1628  /* LCOV_EXCL_STOP */
1629  }
1630 
1631  return pitch_value(LOWEST_PITCH + offset);
1632 }
1633 
1634 static __inline bool is_manual_custos(const gregorio_element *const element)
1635 {
1636  return element->type == GRE_CUSTOS && element->u.misc.pitched.force_pitch;
1637 }
1638 
1644 
1645 static void write_bar(FILE *f, const gregorio_score *const score,
1646  const gregorio_syllable *const syllable,
1647  const gregorio_element *const element,
1648  const unsigned char first_of_disc)
1649 {
1650  const gregorio_bar type = element->u.misc.unpitched.info.bar;
1651  const gregorio_sign signs = element->u.misc.unpitched.special_sign;
1652  const bool is_inside_bar = element->next && !is_manual_custos(element->next)
1653  && element->next->type != GRE_END_OF_LINE;
1654  const bool has_text = !element->previous && syllable->text;
1655  /* the type number of function vepisemaorrare */
1656  const char *offset_case = BarStandard;
1657  signed char far_pitch_adjustment = 0;
1658  /* don't use "In" version of bars in the first argument of a GreDiscretionary */
1659  if (is_inside_bar && first_of_disc != 1) {
1660  fprintf(f, "\\GreIn");
1661  } else {
1662  fprintf(f, "\\Gre");
1663  }
1664  switch (type) {
1665  case B_VIRGULA:
1666  fprintf(f, "Virgula");
1667  offset_case = BarVirgula;
1668  break;
1669  case B_DIVISIO_MINIMA:
1670  fprintf(f, "DivisioMinima");
1671  break;
1672  case B_DIVISIO_MINOR:
1673  fprintf(f, "DivisioMinor");
1674  break;
1675  case B_DIVISIO_MAIOR:
1676  fprintf(f, "DivisioMaior");
1677  break;
1678  case B_DIVISIO_FINALIS:
1679  fprintf(f, "DivisioFinalis");
1680  offset_case = BarDivisioFinalis;
1681  break;
1682  case B_DIVISIO_MINOR_D1:
1683  fprintf(f, "Dominica{1}");
1684  break;
1685  case B_DIVISIO_MINOR_D2:
1686  fprintf(f, "Dominica{2}");
1687  break;
1688  case B_DIVISIO_MINOR_D3:
1689  fprintf(f, "Dominica{3}");
1690  break;
1691  case B_DIVISIO_MINOR_D4:
1692  fprintf(f, "Dominica{4}");
1693  break;
1694  case B_DIVISIO_MINOR_D5:
1695  fprintf(f, "Dominica{5}");
1696  break;
1697  case B_DIVISIO_MINOR_D6:
1698  fprintf(f, "Dominica{6}");
1699  break;
1700  case B_DIVISIO_MINOR_D7:
1701  fprintf(f, "Dominica{7}");
1702  break;
1703  case B_DIVISIO_MINOR_D8:
1704  fprintf(f, "Dominica{8}");
1705  break;
1706  case B_VIRGULA_HIGH:
1707  fprintf(f, "VirgulaHigh");
1708  offset_case = BarVirgula;
1709  break;
1710  case B_DIVISIO_MINIMA_HIGH:
1711  fprintf(f, "DivisioMinimaHigh");
1712  break;
1714  fprintf(f, "DivisioMaiorDotted");
1715  break;
1716  case B_DIVISIO_MINIMIS:
1717  fprintf(f, "DivisioMinimis");
1718  break;
1720  fprintf(f, "DivisioMinimisHigh");
1721  break;
1722  case B_VIRGULA_PAREN:
1723  fprintf(f, "VirgulaParen");
1724  offset_case = BarVirgulaParen;
1725  break;
1726  case B_VIRGULA_PAREN_HIGH:
1727  fprintf(f, "VirgulaParenHigh");
1728  offset_case = BarVirgulaParen;
1729  break;
1731  fprintf(f, "DivisioMinimaParen");
1732  offset_case = BarParen;
1733  break;
1735  fprintf(f, "DivisioMinimaParenHigh");
1736  offset_case = BarParen;
1737  break;
1738  default:
1739  /* not reachable unless there's a programming error */
1740  /* LCOV_EXCL_START */
1741  gregorio_fail2(write_bar, "unknown bar type: %d", type);
1742  break;
1743  /* LCOV_EXCL_STOP */
1744  }
1745  fprintf(f, "{%c}", has_text? '1' : '0');
1746  switch (signs) {
1747  case _V_EPISEMA:
1748  fprintf(f, "{\\GreBarVEpisema{\\GreOCase%s}}", offset_case);
1749  break;
1750  case _BAR_H_EPISEMA:
1751  fprintf(f, "{\\GreBarBrace{\\GreOCase%s}}", offset_case);
1752  break;
1754  fprintf(f, "{\\GreBarBrace{\\GreOCase%s}"
1755  "\\GreBarVEpisema{\\GreOCase%s}}",
1756  offset_case, offset_case);
1757  break;
1758  default:
1759  fprintf(f, "{}");
1760  break;
1761  }
1762  switch (type) {
1764  ++ far_pitch_adjustment;
1765  /* fall through */
1766 
1767  case B_VIRGULA_HIGH:
1768  case B_VIRGULA_PAREN_HIGH:
1769  case B_DIVISIO_MINIMA_HIGH:
1771  ++ far_pitch_adjustment;
1772  /* fall through */
1773 
1774  case B_DIVISIO_MINIMIS:
1775  ++ far_pitch_adjustment;
1776  /* fall through */
1777 
1778  case B_VIRGULA:
1779  case B_VIRGULA_PAREN:
1780  case B_DIVISIO_MINIMA:
1782  {
1783  char is_far = '0';
1784  const gregorio_element *e;
1785  const gregorio_syllable *s;
1786  const gregorio_glyph *g;
1787  const gregorio_note *n;
1788  signed char pitch;
1789 
1790  /* find the prior element */
1791  e = element->previous;
1792  if (!e) {
1793  for (s = syllable->previous_syllable; s; s = s->previous_syllable) {
1794  /* loop to find the previous syllable with elements */
1795  if (s->elements && *s->elements) {
1796  for (e = *s->elements; e->next; e = e->next) {
1797  /* just loop to find the last element */
1798  }
1799  break;
1800  }
1801  }
1802  }
1803  if (e && e->type == GRE_ELEMENT) {
1804  g = e->u.first_glyph;
1805  if (g) {
1806  while (g->next) {
1807  /* loop to find the last glyph in the prior element */
1808  g = g->next;
1809  }
1810  }
1811  if (g && g->type == GRE_GLYPH && (n = g->u.notes.first_note)) {
1812  while (n->next) {
1813  /* loop to find the last note */
1814  n = n->next;
1815  }
1816  pitch = n->u.note.pitch;
1817  if (g->u.notes.liquescentia & L_DEMINUTUS && n->previous
1818  && n->previous->u.note.pitch > pitch) {
1819  pitch = n->previous->u.note.pitch;
1820  }
1821  if (pitch < score->virgula_far_pitch + far_pitch_adjustment) {
1822  /* find next element */
1823  e = element->next;
1824  if (!e) {
1825  for (s = syllable->next_syllable; s;
1826  s = s->next_syllable) {
1827  /* loop to find the next syllable with elements */
1828  if (s->elements && *s->elements) {
1829  e = *s->elements;
1830  break;
1831  }
1832  }
1833  }
1834  if (e) {
1835  if (e->type == GRE_END_OF_LINE) {
1836  is_far = '1';
1837  } else if (e->type == GRE_ELEMENT) {
1838  g = e->u.first_glyph;
1839  if (g && g->type == GRE_GLYPH) {
1840  n = g->u.notes.first_note;
1841  if (n) {
1842  if (g->u.notes.glyph_type == G_PODATUS
1843  && n->next) {
1844  n = n->next;
1845  }
1846  if (n->u.note.pitch
1847  < score->virgula_far_pitch
1848  + far_pitch_adjustment) {
1849  is_far = '1';
1850  }
1851  }
1852  }
1853  }
1854  }
1855  }
1856  }
1857  }
1858  fprintf(f, "{%c}%%\n", is_far);
1859  }
1860  break;
1861 
1862  default:
1863  fprintf(f, "%%\n");
1864  break;
1865  }
1866 }
1867 
1868 static __inline char *suppose_high_ledger_line(const gregorio_note *const note)
1869 {
1870  return note->high_ledger_line? "\\GreSupposeHighLedgerLine" : "";
1871 }
1872 
1873 static __inline char *suppose_low_ledger_line(const gregorio_note *const note)
1874 {
1875  return note->low_ledger_line? "\\GreSupposeLowLedgerLine" : "";
1876 }
1877 
1878 /*
1879  * ! @brief Writes augmentum duplexes (double dots) We suppose we are on the
1880  * last note. \n The algorithm is the following: if there is a previous note,
1881  * we consider that the two puncta of the augumentum duplex must correspond to
1882  * the last note and the previous note. If we are adding to a single note
1883  * glyph, which would be weird but sure why not, we just typeset two puncta
1884  * spaced of 2.
1885  */
1887  const gregorio_note *const current_note)
1888 {
1889  char pitch = current_note->u.note.pitch;
1890  char previous_pitch = 0;
1891  /* second_pitch is the second argument of the \augmentumduplex macro,
1892  * that's what this function is all about. */
1893  char second_pitch = 0;
1894  /* this variable will be set to 1 if we are on the note before the last
1895  * note of a podatus or a porrectus or a torculus resupinus */
1896  unsigned char special_punctum = 0;
1897  if (current_note->previous) {
1899  -1
1901  current_note->u.note.pitch == 1) {
1902  special_punctum = 1;
1903  }
1904  second_pitch = current_note->previous->u.note.pitch;
1905  previous_pitch = second_pitch;
1906  }
1907 
1908  if (!previous_pitch || previous_pitch == pitch) {
1909  if (is_on_a_line(pitch)) {
1910  second_pitch = pitch;
1911  special_punctum = 1;
1912  } else {
1913  second_pitch = pitch + 1;
1914  }
1915  }
1916  /* the first argument should always be the lowest one, that's what we do
1917  * here: */
1918  if (pitch > second_pitch) {
1919  previous_pitch = pitch;
1920  pitch = second_pitch;
1921  second_pitch = previous_pitch;
1922  }
1923  /* maybe the third argument should be changed */
1924  fprintf(f, "\\GreAugmentumDuplex{%d}{%d}{%d}%%\n", pitch_value(pitch),
1925  pitch_value(second_pitch), special_punctum);
1926 }
1927 
1928 /**
1929  * @brief Adds a dot.
1930  *
1931  * Writes \c \\GrePunctumMora in the gtex file, with the appropriate arguments. You might think this function
1932  * more straightforward than it actually is...
1933  */
1935  const gregorio_note *current_note, const int fuse_to_next_note)
1936 {
1937  /* in this if we consider that the puncta are only on the last two notes
1938  * (maybe it would be useful to consider it more entirely, but it would be
1939  * really weird...) */
1940  /* the variable that will be set to true if we have to shift the punctum
1941  * inclinatum before the last note */
1942  bool shift_before = false;
1943  /* this variable will be set to 1 if we are on the note before the last
1944  * note of a podatus or a porrectus or a torculus resupinus */
1945  unsigned char special_punctum = 0;
1946  /* 0 if space is normal, 1 if there should be no space after a punctum */
1947  unsigned char no_space = 0;
1948  /* the pitch where to set the punctum */
1949  char pitch = current_note->u.note.pitch;
1950  /* a variable to know if we are on a punctum inclinatum or not */
1951  unsigned char punctum_inclinatum = 0;
1952  /* a temp variable */
1953  const gregorio_note *tmpnote;
1954  /* we go into this switch only if it is the note before the last note */
1955  if (current_note->next) {
1956  switch (glyph->u.notes.glyph_type) {
1957  case G_FLEXA:
1958  case G_TORCULUS:
1960  case G_PORRECTUS_FLEXUS:
1961  if (!(glyph->u.notes.liquescentia & L_DEMINUTUS)) {
1962  shift_before = true;
1963  }
1964  if (is_between_lines(pitch)
1965  && pitch - current_note->next->u.note.pitch == 1) {
1966  special_punctum = 2;
1967  }
1968  break;
1969  case G_PODATUS:
1970  if ((current_note->u.note.shape != S_PUNCTUM
1972  || (glyph->u.notes.liquescentia
1974  shift_before = true;
1975  /* fine tuning */
1976  if (current_note->next->u.note.pitch -
1977  current_note->u.note.pitch == 1) {
1979  special_punctum = 1;
1980  } else {
1981  pitch = current_note->u.note.pitch - 1;
1982  }
1983  }
1984  } else {
1985  /* case for f.g */
1986  if (current_note->next->u.note.pitch -
1987  current_note->u.note.pitch == 1) {
1988  special_punctum = 1;
1989  }
1990  }
1991  break;
1992  case G_PES_QUADRATUM:
1993  shift_before = true;
1995  1) {
1997  special_punctum = 1;
1998  } else {
1999  pitch = current_note->u.note.pitch - 1;
2000  }
2001  }
2002  break;
2003  case G_PORRECTUS:
2004  case G_TORCULUS_RESUPINUS:
2005  /* this case is only for the note before the previous note */
2006  if ((current_note->next->u.note.pitch -
2007  current_note->u.note.pitch == -1
2008  || current_note->next->u.note.pitch -
2009  current_note->u.note.pitch == 1)
2010  && !(current_note->next->next))
2011  special_punctum = 1;
2012  break;
2013  default:
2014  break;
2015  }
2016  }
2017  /* we enter here in any case */
2018  switch (glyph->u.notes.glyph_type) {
2019  case G_PUNCTA_INCLINATA:
2024  case G_TORCULUS:
2025  if (!current_note->next) {
2026  special_punctum = 1;
2027  }
2028  break;
2029  default:
2030  break;
2031  }
2032  switch (current_note->u.note.shape) {
2037  punctum_inclinatum = 1;
2038  break;
2039  default:
2040  break;
2041  }
2042  /* when the punctum mora is on a note on a line, and the prior note is on
2043  * the space immediately above, the dot is placed on the space below the
2044  * line instead */
2045  if (current_note->previous
2046  && (current_note->previous->u.note.pitch -
2047  current_note->u.note.pitch == 1)
2052  special_punctum = 1;
2053  }
2054 
2056  special_punctum = current_note->mora_vposition == VPOS_BELOW? 1 : 0;
2057  }
2058 
2059  if (shift_before) {
2061  || current_note->next->u.note.pitch -
2062  current_note->u.note.pitch == 1) {
2063  fprintf(f, "\\GrePunctumMora{%d}{3}{%d}{%d}%%\n",
2064  pitch_value(pitch), special_punctum, punctum_inclinatum);
2065  } else {
2066  fprintf(f, "\\GrePunctumMora{%d}{2}{%d}{%d}%%\n",
2067  pitch_value(pitch), special_punctum, punctum_inclinatum);
2068  }
2069  return;
2070  }
2071  /* There are two special cases. The first: if the next glyph is a
2072  * ZERO_WIDTH_SPACE, and the current glyph is a PES, and the punctum mora
2073  * is on the first note, and the first note of the next glyph is at least
2074  * two (or three depending on something) pitches higher than the current
2075  * note.
2076  * You'll all have understood, this case is quite rare... but when it
2077  * appears, we pass 1 as a second argument of \punctummora so that it
2078  * removes the space introduced by the punctummora. */
2079  if (glyph->u.notes.glyph_type == G_PODATUS && glyph->next
2080  && glyph->next->type == GRE_SPACE
2081  && glyph->next->u.misc.unpitched.info.space == SP_ZERO_WIDTH
2082  && current_note->next && glyph->next->next
2083  && glyph->next->next->type == GRE_GLYPH
2084  && glyph->next->next->u.notes.first_note
2085  && (glyph->next->next->u.notes.first_note->u.note.pitch -
2086  current_note->u.note.pitch > 1)) {
2087  fprintf(f, "\\GrePunctumMora{%d}{1}{%d}{%d}%%\n", pitch_value(pitch),
2088  special_punctum, punctum_inclinatum);
2089  return;
2090  }
2091  /* if there is a punctum or a auctum dumplex on a note after, we put a
2092  * zero-width punctum */
2093  tmpnote = current_note->next;
2094  while (tmpnote) {
2095  if (tmpnote->signs == _PUNCTUM_MORA || tmpnote->signs == _AUCTUM_DUPLEX
2096  || tmpnote->signs == _V_EPISEMA_PUNCTUM_MORA
2097  || tmpnote->signs == _V_EPISEMA_AUCTUM_DUPLEX
2098  || tmpnote->choral_sign) {
2099  no_space = 1;
2100  break;
2101  }
2102  tmpnote = tmpnote->next;
2103  }
2104  /* use a special no-space punctum mora for ascending fusion */
2105  if (fuse_to_next_note) {
2106  no_space = 1;
2107  if (fuse_to_next_note > 0) {
2108  special_punctum = 1;
2109  }
2110  }
2111 
2112  /* the normal operation */
2113  fprintf(f, "\\GrePunctumMora{%d}{%d}{%d}{%d}%%\n", pitch_value(pitch),
2114  no_space, special_punctum, punctum_inclinatum);
2115 }
2116 
2118  const gregorio_note *const note)
2119 {
2120  char temp;
2121 
2122  switch (note->u.note.shape) {
2124  if (note->previous) {
2125  /* means that it is not the first note of the puncta inclinata
2126  * sequence */
2127  temp = note->previous->u.note.pitch - note->u.note.pitch;
2128  /* negative values = ascending ambitus */
2129  /* not sure we ever need to consider a larger ambitus here */
2130  switch (temp) {
2131  case 1:
2132  case 2:
2133  case 3:
2134  case 4:
2135  return 25;
2136  case -1:
2137  default:
2138  return 12;
2139  case -2:
2140  return 14;
2141  case -3:
2142  case -4:
2143  return 15;
2144  }
2145  }
2146  break;
2148  if (note->previous) {
2149  /* means that it is not the first note of the puncta inclinata
2150  * sequence */
2151  return 26;
2152  }
2153  break;
2155  if (note->previous) {
2156  /* means that it is not the first note of the puncta inclinata
2157  * sequence */
2158  temp = note->previous->u.note.pitch - note->u.note.pitch;
2159  /* negative values = ascending ambitus */
2160  /* not sure we ever need to consider a larger ambitus here */
2161  switch (temp) {
2162  case 1:
2163  default:
2164  return 3;
2165  case 2:
2166  return 10;
2167  case 3:
2168  case 4:
2169  return 11;
2170  case -1:
2171  case -2:
2172  case -3:
2173  case -4:
2174  return 24;
2175  }
2176  }
2177  break;
2179  if (note->previous) {
2180  /* means that it is not the first note of the puncta inclinata
2181  * sequence */
2182  temp = note->previous->u.note.pitch - note->u.note.pitch;
2183  if (temp < -2) {
2184  return 15;
2185  } else if (temp > 2) {
2186  return 11;
2187  } else {
2188  if (note->previous
2189  && note->previous->u.note.shape ==
2191  if (temp < -1) {
2192  /* really if the ambitus = 3rd at this point */
2193  return 14;
2194  } else if (temp > 1) {
2195  /* really if the ambitus = 3rd at this point */
2196  return 10;
2197  } else {
2198  /* temp == 0, so there is no ascending case */
2199  return 8;
2200  }
2201  } else {
2202  if (temp < 0) {
2203  /* puncta inclinatum followed by puncta inclinatum
2204  * debilis */
2205  return 13;
2206  } else if (temp > 0) {
2207  /* puncta inclinatum followed by puncta inclinatum
2208  * debilis */
2209  return 7;
2210  }
2211  }
2212  }
2213  } /* LCOV_EXCL_LINE */
2214  break;
2216  if (note->previous) {
2217  /* means that it is not the first note of the puncta inclinata
2218  * sequence */
2219  temp = note->previous->u.note.pitch - note->u.note.pitch;
2220  if (temp < -1 || temp > 1) {
2221  /* this is the normal interglyph space, so we'll use it for
2222  * either direction */
2223  return 1;
2224  } else {
2225  /* temp == 0, so there is no ascending case */
2226  /* we approximate that it is the same space */
2227  return 3;
2228  }
2229  }
2230  break;
2231  default:
2232  break;
2233  }
2234 
2235  return -1;
2236 }
2237 
2239  const gregorio_glyph *const glyph)
2240 {
2241  if (glyph->u.notes.glyph_type <= G_PUNCTA_INCLINATA) {
2243  if (next && next->type == GRE_GLYPH
2244  && (next->u.notes.glyph_type == G_PUNCTUM
2245  || (next->u.notes.glyph_type == G_FLEXA
2246  && !next->u.notes.fuse_to_next_glyph))) {
2247  int descent;
2249  descent = note->u.note.pitch -
2250  glyph->next->u.notes.first_note->u.note.pitch;
2251  /* a negative descent is an ascent */
2252  switch(descent) {
2253  case -1:
2254  return 19;
2255  case 1:
2256  return 16;
2257  case -2:
2258  return 20;
2259  case 2:
2260  return 17;
2261  case -3:
2262  case -4:
2263  return 21;
2264  case 3:
2265  case 4:
2266  return 18;
2267  }
2268  }
2269  }
2270 
2271  return -1;
2272 }
2273 
2274 static __inline void write_single_hepisema(FILE *const f, int hepisema_case,
2275  const gregorio_note *const note, gregorio_sign_orientation orientation,
2276  bool connect, char height, const grehepisema_size size, const int i,
2277  const gregorio_glyph *const glyph,
2278  const int porrectus_long_episema_index,
2279  bool (*const is_episema_shown)(const gregorio_note *))
2280 {
2281  if (height) {
2282  const gregorio_hepisema_adjustment *adj =
2284  note->he_adjustment_index[orientation]);
2285  char ambitus = 0;
2286  char size_arg;
2287 
2288  switch (size) {
2289  case H_SMALL_LEFT:
2290  size_arg = 'l';
2291  connect = false;
2292  break;
2293  case H_SMALL_CENTRE:
2294  size_arg = 'c';
2295  connect = false;
2296  break;
2297  case H_SMALL_RIGHT:
2298  size_arg = 'r';
2299  break;
2300  default:
2301  size_arg = 'f';
2302  break;
2303  }
2304 
2305  if (i == porrectus_long_episema_index && note->next
2306  && is_episema_shown(note->next)) {
2307  ambitus = compute_ambitus(note);
2308  }
2309 
2310  if (i - 1 != porrectus_long_episema_index || !note->previous
2311  || !is_episema_shown(note->previous)) {
2312  if (connect) {
2313  const gregorio_glyph *next;
2314  if (!note->next
2316  || next->type != GRE_SPACE
2317  || next->u.misc.unpitched.info.space
2318  != SP_ZERO_WIDTH)) {
2319  /* not followed by a zero-width space */
2320  /* try to fuse from punctum inclinatum to nobar glyph */
2321  fprintf(f,
2322  "\\GreHEpisemaBridge{%d}{%d}{%d}{%s%s}{%s}{%d}%%\n",
2323  pitch_value(height), hepisema_case,
2327  adj->nudge? adj->nudge : "", adj->vbasepos);
2328  } else if (note->next
2329  && (note->next->u.note.shape
2331  || note->next->u.note.shape
2333  || note->next->u.note.shape
2335  || note->next->u.note.shape
2337  /* is a punctum inclinatum of some sort */
2338  fprintf(f, "\\GreHEpisemaBridge{%d}{%d}{%d}{%s%s}{%s}{%d}%%\n",
2339  pitch_value(height), hepisema_case,
2343  adj->nudge? adj->nudge : "", adj->vbasepos);
2344  }
2345  }
2346  fprintf(f, "\\GreHEpisema{%d}{\\GreOCase%s}{%d}{%d}{%c}{%d}"
2347  "{%s%s}{%s}{%d}%%\n", pitch_value(height),
2348  note->gtex_offset_case, ambitus, hepisema_case, size_arg,
2350  suppose_low_ledger_line(note), adj->nudge? adj->nudge : "",
2351  adj->vbasepos);
2352  }
2353  }
2354 }
2355 
2356 /**
2357  * @brief A function that writes the good \c \\hepisema in GregorioTeX.
2358  * @param i The position of the note in the glyph.
2359  */
2360 static void gregoriotex_write_hepisema(FILE *const f,
2361  const gregorio_note *const note, const int i, const gtex_type type,
2362  const gregorio_glyph *const glyph)
2363 {
2364  int porrectus_long_episema_index = -1;
2365 
2367  "called with NULL pointer", return);
2368 
2369  switch (type) {
2370  case T_PORRECTUS:
2371  case T_PORRECTUS_FLEXUS:
2372  porrectus_long_episema_index = 1;
2373  break;
2374  case T_TORCULUS_RESUPINUS:
2376  porrectus_long_episema_index = 2;
2377  break;
2378  default:
2379  /* do nothing */
2380  break;
2381  }
2382 
2385  porrectus_long_episema_index, &gtex_is_h_episema_below_shown);
2388  porrectus_long_episema_index, &gtex_is_h_episema_above_shown);
2389 }
2390 
2391 /* a macro to write an additional line */
2392 
2393 static void write_additional_line(FILE *f, int i, gtex_type type, bool bottom,
2394  const gregorio_note *current_note, const gregorio_score *const score)
2395 {
2396  char ambitus = 0;
2397  gregorio_assert(current_note, write_additional_line, "called with no note",
2398  return);
2399  /* patch to get a line under the full glyph in the case of dbc (for
2400  * example) */
2401  switch (type) {
2402  case T_PORRECTUS:
2403  case T_PORRECTUS_FLEXUS:
2404  switch (i) {
2405  case 1:
2407  break;
2408  case 2:
2411  < score->high_ledger_line_pitch) {
2413  /* HEPISEMA_FIRST_TWO works only for first note */
2415  } else {
2416  return;
2417  }
2418  break;
2419  case 3:
2421  >= score->high_ledger_line_pitch) {
2422  /* we don't need to add twice the same line */
2423  return;
2424  }
2425  break;
2426  }
2427  break;
2428  case T_TORCULUS_RESUPINUS:
2430  switch (i) {
2431  case 2:
2433  break;
2434  case 3:
2437  < score->high_ledger_line_pitch) {
2439  /* HEPISEMA_FIRST_TWO works only for first note */
2441  } else {
2442  return;
2443  }
2444  break;
2445  case 4:
2447  >= score->high_ledger_line_pitch) {
2448  /* we don't need to add twice the same line */
2449  return;
2450  }
2451  break;
2452  }
2453  break;
2454  default:
2455  break;
2456  }
2457 
2458  if (i == HEPISEMA_FIRST_TWO) {
2459  /* here we must compare the first note of the big bar with the second
2460  * one, but it may be tricky sometimes, because of the previous patch */
2464  write_additional_line, "HEPISEMA_FIRST_TWO on the wrong note");
2465  ambitus = current_note->u.note.pitch - current_note->next->u.note.pitch;
2466  }
2467  fprintf(f, "\\GreAdditionalLine{\\GreOCase%s}{%d}{%d}%%\n",
2468  current_note->gtex_offset_case, ambitus, bottom ? 3 : 2);
2469 }
2470 
2471 /*
2472  *
2473  * a function that writes the good value of \vepisema in GregorioTeX. i is the
2474  * position of the note in the glyph
2475  *
2476  */
2477 
2478 static void write_vepisema(FILE *f, const gregorio_note *const note)
2479 {
2480  fprintf(f, "\\GreVEpisema{%d}{\\GreOCase%s}%%\n",
2482 }
2483 
2484 /*
2485  * a function that writes the rare signs in GregorioTeX. i is the position of
2486  * the note in the glyph */
2487 
2488 static void write_rare_sign(FILE *f, const gregorio_note *const current_note,
2489  const gregorio_sign rare)
2490 {
2491  switch (rare) {
2492  case _ACCENTUS:
2493  fprintf(f, "\\GreAccentus{%d}{\\GreOCase%s}%%\n",
2496  break;
2497  case _ACCENTUS_REVERSUS:
2498  fprintf(f, "\\GreReversedAccentus{%d}{\\GreOCase%s}%%\n",
2501  break;
2502  case _CIRCULUS:
2503  fprintf(f, "\\GreCirculus{%d}{\\GreOCase%s}%%\n",
2506  break;
2507  case _SEMI_CIRCULUS:
2508  fprintf(f, "\\GreSemicirculus{%d}{\\GreOCase%s}%%\n",
2511  break;
2513  fprintf(f, "\\GreReversedSemicirculus{%d}{\\GreOCase%s}%%\n",
2516  break;
2517  case _MUSICA_FICTA_FLAT:
2518  fprintf(f, "\\GreMusicaFictaFlat{%d}{\\GreOCase%s}%%\n",
2521  break;
2522  case _MUSICA_FICTA_NATURAL:
2523  fprintf(f, "\\GreMusicaFictaNatural{%d}{\\GreOCase%s}%%\n",
2526  break;
2527  case _MUSICA_FICTA_SHARP:
2528  fprintf(f, "\\GreMusicaFictaSharp{%d}{\\GreOCase%s}%%\n",
2531  break;
2532  /* the cases of the bar signs are dealt in another function
2533  * (write_bar) */
2534  default:
2535  /* not reachable unless there's a programming error */
2536  /* LCOV_EXCL_START */
2537  assert(false);
2538  break;
2539  /* LCOV_EXCL_STOP */
2540  }
2541 }
2542 
2543 static __inline const char *alteration_name(
2544  const gregorio_shape next_note_alteration)
2545 {
2546  switch (next_note_alteration) {
2547  case S_FLAT:
2548  return "Flat";
2549  case S_FLAT_PAREN:
2550  return "FlatParen";
2551  case S_SHARP:
2552  return "Sharp";
2553  case S_SHARP_PAREN:
2554  return "SharpParen";
2555  case S_NATURAL:
2556  return "Natural";
2557  case S_NATURAL_PAREN:
2558  return "NaturalParen";
2559  default:
2560  return "";
2561  }
2562 }
2563 
2564 static const char *next_custos(const signed char next_note_pitch,
2565  const gregorio_shape next_note_alteration,
2566  gregoriotex_status *const status)
2567 {
2568  static char buf[50];
2569 
2570  gregorio_snprintf(buf, sizeof buf, "%s\\GreNextCustos{%d}{%s}",
2571  status->suppressed_custos? "\\GreResetEolCustos" : "",
2572  pitch_value(next_note_pitch),
2573  alteration_name(next_note_alteration));
2574 
2575  status->suppressed_custos = false;
2576 
2577  return buf;
2578 }
2579 
2580 /*
2581  * function used when the glyph is only one note long
2582  */
2583 
2584 static void write_note(FILE *f, gregorio_note *note,
2585  const gregorio_glyph *glyph, const char next_note_pitch,
2586  const gregorio_shape next_note_alteration,
2587  gregoriotex_status *const status)
2588 {
2589  unsigned int initial_shape = note->u.note.shape;
2590  const char *shape;
2591  int space_case;
2592  static char cpbuf[96];
2593  /* type in the sense of GregorioTeX alignment type */
2595  gregorio_assert(note, write_note, "called with NULL pointer",
2596  return);
2597  if (note->u.note.shape == S_PUNCTUM) {
2598  switch (note->u.note.liquescentia) {
2599  case L_AUCTUS_ASCENDENS:
2601  break;
2602  case L_AUCTUS_DESCENDENS:
2604  break;
2605  case L_INITIO_DEBILIS:
2606  if (glyph->u.notes.fuse_to_next_glyph > 0) {
2607  break;
2608  }
2609  /* fall through */
2610  case L_DEMINUTUS:
2611  /* this is a currenly unused, but we keep it as a fallback case */
2612  /* LCOV_EXCL_START */
2613  note->u.note.shape = S_PUNCTUM_DEMINUTUS;
2614  break;
2615  /* LCOV_EXCL_STOP */
2616  default:
2617  break;
2618  }
2619  }
2620  shape = determine_note_glyph_name(note, glyph, &type);
2621  note->u.note.shape = initial_shape;
2622  /* special things for puncta inclinata */
2623  space_case = get_punctum_inclinatum_space_case(note);
2624  if (space_case >= 0) {
2625  fprintf(f, "\\GreEndOfGlyph{%d}%%\n", space_case);
2626  }
2627 
2628  switch (note->u.note.shape) {
2629  case S_FLAT:
2630  case S_FLAT_PAREN:
2631  case S_NATURAL:
2632  case S_NATURAL_PAREN:
2633  case S_SHARP:
2634  case S_SHARP_PAREN:
2635  fprintf(f, "\\Gre%s{%d}{0}", alteration_name(note->u.note.shape),
2636  pitch_value(note->u.note.pitch));
2637  break;
2638  default:
2639  fprintf(f, "\\GreGlyph{%s}{%d}{%s}{%d}",
2640  code_point(shape, glyph->u.notes.is_cavum, cpbuf, sizeof cpbuf),
2641  pitch_value(note->u.note.pitch),
2642  next_custos(next_note_pitch, next_note_alteration, status),
2643  type);
2644  break;
2645  }
2646 }
2647 
2649  gtex_alignment *type, gtex_alteration *alteration)
2650 {
2651  gtex_type gtype = T_ONE_NOTE;
2652  /* alteration says if there is a flat or a natural first in the next
2653  * syllable, see gregoriotex.tex for more details */
2656  gregorio_assert(type && alteration && syllable && syllable->elements,
2657  syllable_first_type, "called with a NULL argument", return);
2658  *type = AT_ONE_NOTE;
2659  *alteration = ALT_NONE;
2660  for (element = syllable->elements[0]; element; element = element->next) {
2661  if (element->type == GRE_BAR) {
2662  switch (element->u.misc.unpitched.info.bar) {
2663  case B_VIRGULA:
2664  case B_VIRGULA_HIGH:
2665  *type = AT_VIRGULA;
2666  break;
2667  case B_VIRGULA_PAREN:
2668  case B_VIRGULA_PAREN_HIGH:
2670  break;
2671  case B_DIVISIO_MINIMIS:
2673  case B_DIVISIO_MINIMA:
2674  case B_DIVISIO_MINIMA_HIGH:
2676  break;
2680  break;
2681  case B_DIVISIO_MINOR:
2683  break;
2684  case B_DIVISIO_MAIOR:
2687  break;
2688  case B_DIVISIO_MINOR_D1:
2689  case B_DIVISIO_MINOR_D2:
2690  case B_DIVISIO_MINOR_D3:
2691  case B_DIVISIO_MINOR_D4:
2692  case B_DIVISIO_MINOR_D5:
2693  case B_DIVISIO_MINOR_D6:
2694  case B_DIVISIO_MINOR_D7:
2695  case B_DIVISIO_MINOR_D8:
2697  break;
2698  case B_DIVISIO_FINALIS:
2700  break;
2701  default:
2702  /* not reachable unless there's a programming error */
2703  /* LCOV_EXCL_START */
2704  gregorio_fail(syllable_first_type, "invalid bar");
2705  *type = AT_ONE_NOTE;
2706  break;
2707  /* LCOV_EXCL_STOP */
2708  }
2709  *alteration = ALT_NONE;
2710  return;
2711  }
2712  if (element->type == GRE_ELEMENT && element->u.first_glyph) {
2713  for (glyph = element->u.first_glyph; glyph; glyph = glyph->next) {
2714  if (glyph->type == GRE_GLYPH && glyph->u.notes.first_note) {
2715  switch (glyph->u.notes.glyph_type) {
2716  case G_ALTERATION:
2717  if (*alteration == ALT_NONE) {
2718  switch (glyph->u.notes.first_note->u.note.shape) {
2719  case S_FLAT:
2720  *alteration = ALT_FLAT;
2721  break;
2722  case S_FLAT_PAREN:
2723  *alteration = ALT_FLAT_PAREN;
2724  break;
2725  case S_NATURAL:
2726  *alteration = ALT_NATURAL;
2727  break;
2728  case S_NATURAL_PAREN:
2729  *alteration = ALT_NATURAL_PAREN;
2730  break;
2731  case S_SHARP:
2732  *alteration = ALT_SHARP;
2733  break;
2734  case S_SHARP_PAREN:
2735  *alteration = ALT_SHARP_PAREN;
2736  break;
2737  default:
2738  /* not reachable unless there's a programming error */
2739  /* LCOV_EXCL_START */
2741  "invalid alteration");
2742  break;
2743  /* LCOV_EXCL_STOP */
2744  }
2745  }
2746  continue;
2747  case G_PUNCTA_INCLINATA:
2756  case G_PUNCTUM:
2757  case G_STROPHA:
2758  case G_VIRGA:
2759  case G_VIRGA_REVERSA:
2760  case G_STROPHA_AUCTA:
2761  case G_DISTROPHA:
2762  case G_DISTROPHA_AUCTA:
2763  case G_TRISTROPHA:
2764  case G_TRISTROPHA_AUCTA:
2765  case G_BIVIRGA:
2766  case G_TRIVIRGA:
2768  (glyph->u.notes.first_note, glyph, type);
2769  break;
2770  default:
2772  break;
2773  }
2774  return;
2775  }
2776  }
2777  }
2778  }
2779  if (syllable->elements[0]) {
2780  /* if there is anything else in the next syllable */
2781  *alteration = ALT_NONE;
2782  *type = AT_ONE_NOTE;
2783  return;
2784  }
2785  /* the only thing that should reach this point is an empty next syllable
2786  * we treat that kind of syllable as a bar */
2787  *alteration = ALT_NONE;
2789  return;
2790 }
2791 
2792 static __inline void write_low_choral_sign(FILE *const f,
2793  const gregorio_note *const note, int special)
2794 {
2795  if (note->choral_sign_is_nabc) {
2796  fprintf(f, "\\GreLowChoralSign{%d}{\\GreNABCChar{",
2797  pitch_value(note->u.note.pitch));
2798  tex_escape_text(f, note->choral_sign);
2799  fprintf(f, "}}{%d}%%\n", special);
2800  } else {
2801  fprintf(f, "\\GreLowChoralSign{%d}{%s}{%d}%%\n",
2802  pitch_value(note->u.note.pitch), note->choral_sign,
2803  special);
2804  }
2805 }
2806 
2807 static __inline void write_high_choral_sign(FILE *const f,
2808  const gregorio_note *const note, int pitch_offset)
2809 {
2810  if (note->choral_sign_is_nabc) {
2811  fprintf(f, "\\GreHighChoralSign{%d}{\\GreNABCChar{",
2812  pitch_value(note->u.note.pitch + pitch_offset));
2813  tex_escape_text(f, note->choral_sign);
2814  fprintf(f, "}}{\\GreOCase%s}%%\n", note->gtex_offset_case);
2815  } else {
2816  fprintf(f, "\\GreHighChoralSign{%d}{%s}{\\GreOCase%s}%%\n",
2817  pitch_value(note->u.note.pitch + pitch_offset),
2818  note->choral_sign, note->gtex_offset_case);
2819  }
2820 }
2821 
2823  const gregorio_note *current_note, bool low)
2824 {
2825  bool kind_of_pes;
2826  /* false in the normal case (sign above the note), true in the case of it's
2827  * next to the note (same height as a punctum) */
2828  bool low_sign = choral_sign_here_is_low(glyph, current_note, &kind_of_pes);
2829 
2830  /* the low choral signs must be typeset after the punctum, whereas the high
2831  * must be typeset before the h episema */
2832  if ((low_sign && !low) || (!low_sign && low)) {
2833  return;
2834  }
2835 
2836  if (low_sign) {
2837  /* very approximative heuristic, some things may have to be adapted
2838  * here... */
2840  if (kind_of_pes && current_note->u.note.pitch -
2841  current_note->next->u.note.pitch == -1) {
2843  return;
2844  }
2845  if (current_note->previous
2847  || current_note->previous->signs ==
2850  return;
2851  }
2852  }
2853 
2855  } else {
2856  /* let's cheat a little */
2859  } else {
2861  }
2862  }
2863 }
2864 
2865 /*
2866  *
2867  * A function that write the signs of a glyph, which has the type type (T_*,
2868  * not G_*, which is in the glyph->glyph_type), and (important), we start only
2869  * at the note current_note. It is due to the way we call it : if type is
2870  * T_ONE_NOTE, we just do the signs on current_note, not all. This is the case
2871  * for example for the first note of the torculus resupinus, or the
2872  * G_*_PUNCTA_INCLINATA.
2873  *
2874  */
2875 
2876 /* small helper */
2877 static __inline bool _found(FILE *const f, const bool found)
2878 {
2879  if (!found) {
2880  fprintf (f, "%%\n");\
2881  return true;
2882  }
2883  return found;
2884 }
2885 
2887  const gregorio_note *note, signed char *const top_height,
2888  signed char *const bottom_height)
2889 {
2890  char height;
2891  /* get the minima/maxima pitches */
2892  for (; note; note = note->next) {
2893  if (note->h_episema_above) {
2894  height = note->h_episema_above;
2895  } else {
2896  if (note->v_episema_height && note->v_episema_height
2897  > note->u.note.pitch) {
2898  height = note->v_episema_height;
2899  } else {
2900  height = note->u.note.pitch;
2901  }
2902 
2903  if (note->choral_sign
2904  && !choral_sign_here_is_low(glyph, note, NULL)) {
2905  ++height;
2906  }
2907  }
2909  *top_height = height;
2910  }
2911 
2912  if (note->h_episema_below) {
2913  height = note->h_episema_below;
2914  } else if (note->v_episema_height && note->v_episema_height
2915  < note->u.note.pitch) {
2916  height = note->v_episema_height;
2917  } else {
2918  height = note->u.note.pitch;
2919 
2920  /* no vertical episema, so we need to check for a stem */
2922  "glyph->type must be GRE_GLYPH here", return);
2923  switch (glyph->u.notes.glyph_type) {
2924  case G_BIVIRGA:
2925  case G_TRIVIRGA:
2926  case G_VIRGA:
2927  case G_VIRGA_REVERSA:
2928  --height;
2929  break;
2930 
2931  case G_PUNCTUM:
2932  switch (note->u.note.shape) {
2935  --height;
2936  break;
2937 
2938  default:
2939  /* default to avoid the warning */
2940  break;
2941  }
2942  break;
2943 
2944  default:
2945  /* default to avoid the warning */
2946 
2947  /* other shapes like the porrectus have stems, but the height
2948  * computation will cover other notes in the shape, negating
2949  * the need to account for the stem on the shape */
2950  break;
2951  }
2952  }
2953 
2954  if (*bottom_height == UNDETERMINED_HEIGHT || height < *bottom_height) {
2955  *bottom_height = height;
2956  }
2957  }
2958 }
2959 
2961  const gregorio_element *const element, signed char *const top_height,
2962  signed char *const bottom_height)
2963 {
2964  const gregorio_glyph *glyph;
2966  if (element->type != GRE_ELEMENT) {
2967  return;
2968  }
2969  /* get the minima/maxima pitches */
2970  for (glyph = element->u.first_glyph; glyph; glyph = glyph->next) {
2971  if (glyph->type == GRE_GLYPH) {
2972  compute_height_extrema(glyph, glyph->u.notes.first_note,
2973  top_height, bottom_height);
2974  }
2975  }
2976 }
2977 
2978 static __inline void fixup_height_extrema(signed char *const top_height,
2979  signed char *const bottom_height)
2980 {
2981  if (*top_height == UNDETERMINED_HEIGHT) {
2982  *top_height = 9; /* 'g' */
2983  }
2984  if (*bottom_height == UNDETERMINED_HEIGHT) {
2985  *bottom_height = 9; /* 'g' */
2986  }
2987 }
2988 
2989 static __inline bool is_ledger_drawn(const bool setting,
2990  const gregorio_ledger_specificity specificity)
2991 {
2992  return ((specificity & LEDGER_DRAWN) && setting);
2993 }
2994 
2996  const gregorio_glyph *glyph, const gregorio_note *note,
2997  int fuse_to_next_note, gregoriotex_status *const status,
2998  const gregorio_score *const score)
2999 {
3000  /* i is the number of the note for which we are typesetting the sign. */
3001  int i;
3002  const gregorio_note *current_note;
3003  signed char high_pitch = UNDETERMINED_HEIGHT;
3004  signed char low_pitch = UNDETERMINED_HEIGHT;
3005  bool found = false;
3006  compute_height_extrema(glyph, note, &high_pitch, &low_pitch);
3007  fixup_height_extrema(&high_pitch, &low_pitch);
3008  fprintf(f, "%%\n{%%\n\\GreGlyphHeights{%d}{%d}%%\n",
3009  pitch_value(high_pitch), pitch_value(low_pitch));
3010  for (current_note = note, i = 1; current_note;
3011  current_note = current_note->next, ++i) {
3012  /* we start by the additional lines */
3016  status->bottom_line = 1;
3017  }
3021  }
3022  if (current_note->texverb) {
3023  fprintf(f, "%% verbatim text at note level:\n%s%%\n"
3024  "%% end of verbatim text\n",
3026  }
3027  if (type == T_ONE_NOTE) {
3028  break;
3029  }
3030  }
3031  fprintf(f, "}{");
3032  /* now a first loop for the choral signs, because high signs must be taken
3033  * into account before any hepisema */
3034  for (current_note = note, i = 1; current_note;
3035  current_note = current_note->next, ++i) {
3036  if (current_note->choral_sign) {
3037  found = _found(f, found);
3039  }
3040  if (type == T_ONE_NOTE) {
3041  break;
3042  }
3043  }
3044  /* a loop for rare signs, vertical episema, and horizontal episema */
3045  for (current_note = note, i = 1; current_note;
3046  current_note = current_note->next, ++i) {
3047  /* we continue with the hepisema */
3049  found = _found(f, found);
3051  }
3052  /* write_rare_sign also writes the vepisema */
3053  if (current_note->special_sign) {
3054  found = _found(f, found);
3056  }
3057  if (current_note->signs != _NO_SIGN) {
3058  found = _found(f, found);
3059  }
3060  switch (current_note->signs) {
3061  case _V_EPISEMA:
3065  break;
3066  default:
3067  /* do nothing */
3068  break;
3069  }
3070  if (type == T_ONE_NOTE) {
3071  break;
3072  }
3073  }
3074  /* final loop for choral signs and punctum mora */
3075  for (current_note = note, i = 1; current_note;
3076  current_note = current_note->next, ++i) {
3077  switch (current_note->signs) {
3078  case _PUNCTUM_MORA:
3080  write_punctum_mora(f, glyph, current_note, fuse_to_next_note);
3081  break;
3082  case _AUCTUM_DUPLEX:
3085  break;
3086  default:
3087  break;
3088  }
3089  if (current_note->choral_sign) {
3090  found = _found(f, found);
3092  }
3093  if (type == T_ONE_NOTE) {
3094  break;
3095  }
3096  }
3097  fprintf(f, "}{");
3098  if (status->point_and_click) {
3099  fprintf(f, "%u:%u:%u", note->src_line, note->src_offset,
3100  note->src_column + 1);
3101  }
3102  fprintf(f, "}%%\n");
3103 }
3104 
3106 {
3107  static char buf[BUFSIZE];
3108  int ambitus = compute_ambitus(glyph->u.notes.first_note);
3109  const char *head, *head_liquescence;
3110 
3111  switch (glyph->u.notes.first_note->u.note.shape) {
3112  case S_QUILISMA:
3113  head = "Quilisma";
3114  break;
3115  case S_ORISCUS_ASCENDENS:
3116  case S_ORISCUS_DESCENDENS:
3117  head = "Oriscus";
3118  break;
3119  default:
3120  head = "Punctum";
3121  break;
3122  }
3123 
3124  if (glyph->u.notes.liquescentia & L_INITIO_DEBILIS) {
3125  head_liquescence = "InitioDebilis";
3126  } else {
3127  head_liquescence = "";
3128  }
3129 
3130  gregorio_snprintf(buf, BUFSIZE, "Leading%s%s%s", head, tex_ambitus[ambitus],
3131  head_liquescence);
3132  return buf;
3133 }
3134 
3135 static __inline void write_composed_multinote_glyph(FILE *const f,
3137  const signed char next_note_pitch,
3138  const gregorio_shape next_note_alteration,
3139  const int fuse_to_next_note, gregoriotex_status *const status,
3140  const gregorio_score *const score, const char *glyph_end_between_notes)
3141 {
3142  while (current_note) {
3143  const bool has_next = current_note->next != NULL;
3144  if (has_next) {
3147  } else {
3148  write_note(f, current_note, glyph, next_note_pitch,
3149  next_note_alteration, status);
3150  }
3152  has_next ? 0 : fuse_to_next_note, status, score);
3154  if (current_note && glyph_end_between_notes) {
3155  fputs(glyph_end_between_notes, f);
3156  }
3157  }
3158 }
3159 
3160 static void write_glyph(FILE *const f, const gregorio_syllable *const syllable,
3161  const gregorio_element *const element,
3162  gregorio_glyph *const glyph,
3163  gregoriotex_status *const status, const gregorio_score *const score)
3164 {
3165  static char cpbuf[96], cpbuf2[96];
3166  /* type is the type of the glyph. Understand the type of the glyph for
3167  * gregoriotex, for the alignement between text and notes. (AT_ONE_NOTE,
3168  * etc.) */
3169  gtex_alignment type = 0;
3170  /* the type of the glyph, in the sense of the shape (T_PES, etc.) */
3171  gtex_type gtype = 0;
3172  signed char next_note_pitch = 0;
3174  const char *leading_shape, *shape;
3175  const gregorio_glyph *prev_glyph =
3177  int fuse_to_next_note, fuse_from_previous_note =
3178  (prev_glyph && prev_glyph->type == GRE_GLYPH)
3179  ? prev_glyph->u.notes.fuse_to_next_glyph : 0;
3180  gregorio_shape next_note_alteration;
3181  gregorio_assert(glyph, write_glyph, "called with NULL pointer", return);
3182  gregorio_assert(glyph->type == GRE_GLYPH && glyph->u.notes.first_note,
3183  write_glyph, "called with glyph without note", return);
3184  fuse_to_next_note = glyph->u.notes.fuse_to_next_glyph;
3185  if (fuse_from_previous_note) {
3186  fprintf(f, "\\GreFuse");
3187  }
3189  &next_note_alteration);
3190  current_note = glyph->u.notes.first_note;
3191  /* first we check if it is really a unique glyph in gregoriotex... the
3192  * glyphs that are not a unique glyph are : trigonus and pucta inclinata
3193  * in general, and torculus resupinus and torculus resupinus flexus, so
3194  * we first divide the glyph into real gregoriotex glyphs */
3195  switch (glyph->u.notes.glyph_type) {
3196  case G_PUNCTA_INCLINATA:
3206  next_note_pitch, next_note_alteration, fuse_to_next_note,
3207  status, score, NULL);
3208  break;
3209  case G_SCANDICUS:
3210  if ((glyph->u.notes.liquescentia & L_DEMINUTUS)
3211  || glyph->u.notes.liquescentia == L_NO_LIQUESCENTIA
3212  || glyph->u.notes.liquescentia == L_FUSED) {
3213  shape = gregoriotex_determine_glyph_name(glyph, &type, &gtype);
3214  fprintf(f, "\\GreGlyph{%s}{%d}{%s}{%d}", code_point(shape,
3215  glyph->u.notes.is_cavum, cpbuf, sizeof cpbuf),
3216  pitch_value(glyph->u.notes.first_note->u.note.pitch),
3217  next_custos(next_note_pitch, next_note_alteration, status),
3218  type);
3219  write_signs(f, gtype, glyph, glyph->u.notes.first_note,
3220  fuse_to_next_note, status, score);
3221  } else {
3223  next_note_pitch, next_note_alteration, fuse_to_next_note,
3224  status, score, NULL);
3225  }
3226  break;
3227  case G_ANCUS:
3228  gregorio_assert(glyph->u.notes.liquescentia & L_DEMINUTUS,
3229  write_glyph, "encountered a non-deminutus ancus", break);
3230  shape = gregoriotex_determine_glyph_name(glyph, &type, &gtype);
3231  fprintf(f, "\\GreGlyph{%s}{%d}{%s}{%d}", code_point(shape,
3232  glyph->u.notes.is_cavum, cpbuf, sizeof cpbuf),
3233  pitch_value(glyph->u.notes.first_note->u.note.pitch),
3234  next_custos(next_note_pitch, next_note_alteration, status),
3235  type);
3236  write_signs(f, gtype, glyph, glyph->u.notes.first_note,
3237  fuse_to_next_note, status, score);
3238  break;
3240  /* we retain this "old-style" fusion as it does look marginally better
3241  * on screen */
3242  leading_shape = determine_leading_shape(glyph);
3243  /* trick to have the good position for these glyphs */
3244  glyph->u.notes.glyph_type = G_PORRECTUS_FLEXUS_NO_BAR;
3245  glyph->u.notes.first_note = current_note->next;
3246  shape = gregoriotex_determine_glyph_name(glyph, &type, &gtype);
3247  fprintf(f, "\\GreGlyph{\\GreFuseTwo{%s}{%s}}{%d}{%s}{%d}",
3248  code_point(leading_shape, glyph->u.notes.is_cavum, cpbuf,
3249  sizeof cpbuf),
3250  code_point(shape, glyph->u.notes.is_cavum, cpbuf2,
3251  sizeof cpbuf2),
3252  pitch_value(glyph->u.notes.first_note->u.note.pitch),
3253  next_custos(next_note_pitch, next_note_alteration, status),
3254  type);
3255  glyph->u.notes.first_note = current_note;
3256  glyph->u.notes.glyph_type = G_TORCULUS_RESUPINUS_FLEXUS;
3257  write_signs(f, gtype, glyph, glyph->u.notes.first_note,
3258  fuse_to_next_note, status, score);
3259  break;
3260  case G_BIVIRGA:
3261  case G_TRIVIRGA:
3263  next_note_pitch, next_note_alteration, fuse_to_next_note,
3264  status, score, "\\GreEndOfGlyph{4}%%\n");
3265  break;
3266  case G_TRISTROPHA:
3267  case G_TRISTROPHA_AUCTA:
3268  case G_DISTROPHA:
3269  case G_DISTROPHA_AUCTA:
3271  next_note_pitch, next_note_alteration, fuse_to_next_note,
3272  status, score, "\\GreEndOfGlyph{5}%%\n");
3273  break;
3274 
3275  case G_PUNCTUM:
3276  switch (glyph->u.notes.first_note->u.note.shape) {
3277  case S_ORISCUS_ASCENDENS:
3278  case S_ORISCUS_DESCENDENS:
3279  case S_ORISCUS_DEMINUTUS:
3282  /* don't change the oriscus */
3283  break;
3284 
3285  default:
3286  switch (glyph->u.notes.liquescentia) {
3287  case L_AUCTUS_ASCENDENS:
3288  glyph->u.notes.first_note->u.note.shape =
3290  break;
3291  case L_AUCTUS_DESCENDENS:
3292  glyph->u.notes.first_note->u.note.shape =
3294  break;
3295  case L_INITIO_DEBILIS:
3296  if (glyph->u.notes.fuse_to_next_glyph > 0) {
3297  break;
3298  }
3299  /* fall through */
3300  case L_DEMINUTUS:
3301  glyph->u.notes.first_note->u.note.shape = S_PUNCTUM_DEMINUTUS;
3302  default:
3303  break;
3304  }
3305  break;
3306  }
3307 
3308  /* fall through */
3309  case G_PUNCTUM_INCLINATUM:
3310  case G_VIRGA:
3311  case G_VIRGA_REVERSA:
3312  case G_STROPHA:
3313  case G_STROPHA_AUCTA:
3314  case G_ALTERATION:
3315  write_note(f, glyph->u.notes.first_note, glyph, next_note_pitch,
3316  next_note_alteration, status);
3317  write_signs(f, T_ONE_NOTE, glyph, current_note, fuse_to_next_note,
3318  status, score);
3319  break;
3320  default:
3321  /* we retain this "old-style" fusion as it does look marginally better
3322  * on screen */
3323  /* special case of the torculus resupinus which first note is not a
3324  * punctum */
3325  if (glyph->u.notes.glyph_type == G_TORCULUS_RESUPINUS
3327  && current_note->u.note.shape != S_QUILISMA) {
3328  leading_shape = determine_leading_shape(glyph);
3329  /* trick to have the good position for these glyphs */
3330  glyph->u.notes.glyph_type = G_PORRECTUS_NO_BAR;
3331  glyph->u.notes.first_note = current_note->next;
3332  shape = gregoriotex_determine_glyph_name(glyph, &type, &gtype);
3333  fprintf(f, "\\GreGlyph{\\GreFuseTwo{%s}{%s}}{%d}{%s}{%d}",
3334  code_point(leading_shape, glyph->u.notes.is_cavum, cpbuf,
3335  sizeof cpbuf),
3336  code_point(shape, glyph->u.notes.is_cavum, cpbuf2,
3337  sizeof cpbuf2),
3338  pitch_value(glyph->u.notes.first_note->u.note.pitch),
3339  next_custos(next_note_pitch, next_note_alteration, status),
3340  type);
3341  glyph->u.notes.first_note = current_note;
3342  glyph->u.notes.glyph_type = G_TORCULUS_RESUPINUS;
3343  write_signs(f, gtype, glyph, glyph->u.notes.first_note,
3344  fuse_to_next_note, status, score);
3345  break;
3346  } else {
3347  shape = gregoriotex_determine_glyph_name(glyph, &type, &gtype);
3348  fprintf(f, "\\GreGlyph{%s}{%d}{%s}{%d}", code_point(shape,
3349  glyph->u.notes.is_cavum, cpbuf, sizeof cpbuf),
3350  pitch_value(glyph->u.notes.first_note->u.note.pitch),
3351  next_custos(next_note_pitch, next_note_alteration, status),
3352  type);
3353  write_signs(f, gtype, glyph, glyph->u.notes.first_note,
3354  fuse_to_next_note, status, score);
3355  break;
3356  }
3357  }
3358 }
3359 
3360 static __inline unsigned int glyph_note_units(const gregorio_glyph *glyph)
3361 {
3362  unsigned int count = 0;
3363  const gregorio_note *note;
3364  if (glyph->u.notes.glyph_type != G_ALTERATION) {
3365  for (note = glyph->u.notes.first_note; note; note = note->next) {
3366  ++count;
3367  }
3368  }
3369  return count;
3370 }
3371 
3372 /* here we absolutely need to pass the syllable as an argument, because we
3373  * will need the next note, that may be contained in the next syllable */
3374 
3375 static unsigned int write_element(FILE *const f,
3376  const gregorio_syllable *const syllable,
3377  const gregorio_element *const element,
3378  gregoriotex_status *const status,
3379  const gregorio_score *const score)
3380 {
3381  unsigned int note_unit_count = 0;
3382  if (element->type == GRE_ELEMENT) {
3384  for (glyph = element->u.first_glyph; glyph; glyph = glyph->next) {
3385  switch (glyph->type) {
3386  case GRE_SPACE:
3387  switch (glyph->u.misc.unpitched.info.space) {
3388  case SP_ZERO_WIDTH:
3389  fprintf(f, "\\GreEndOfGlyph{1}%%\n");
3390  break;
3391  case SP_HALF_SPACE:
3392  fprintf(f, "\\GreEndOfGlyph{22}%%\n");
3393  break;
3394  case SP_INTERGLYPH_SPACE:
3395  fprintf(f, "\\GreEndOfGlyph{0}%%\n");
3396  break;
3397  default:
3398  /* not reachable unless there's a programming error */
3399  /* LCOV_EXCL_START */
3401  "encountered an unexpected glyph-level space");
3402  break;
3403  /* LCOV_EXCL_STOP */
3404  }
3405  break;
3406 
3407  case GRE_TEXVERB_GLYPH:
3408  if (glyph->texverb) {
3409  fprintf(f, "%% verbatim text at glyph level:\n%s%%\n"
3410  "%% end of verbatim text\n",
3411  gregorio_texverb(glyph->texverb));
3412  }
3413  break;
3414 
3415  default:
3416  /* at this point glyph->type is GRE_GLYPH */
3417  assert(glyph->type == GRE_GLYPH);
3419  note_unit_count += glyph_note_units(glyph);
3420  if (glyph->next && glyph->next->type == GRE_GLYPH &&
3421  glyph->next->u.notes.glyph_type != G_ALTERATION) {
3422  if (is_fused(glyph->next->u.notes.liquescentia)) {
3423  int space_case =
3425  if (space_case >= 0) {
3426  /* fuse from punctum inclinatum to nobar glyph */
3427  fprintf(f, "\\GreEndOfGlyph{%d}%%\n", space_case);
3428  } else {
3429  fprintf(f, "\\GreEndOfGlyph{1}%%\n");
3430  }
3431  } else if (is_puncta_inclinata(
3432  glyph->next->u.notes.glyph_type)
3433  || glyph->next->u.notes.glyph_type
3434  == G_PUNCTA_INCLINATA) {
3435  if ((is_puncta_inclinata(glyph->u.notes.glyph_type)
3436  || glyph->u.notes.glyph_type
3437  == G_PUNCTA_INCLINATA)
3438  && glyph->next->u.notes.first_note->u.note.pitch
3439  == gregorio_glyph_last_note(glyph)->u.note.pitch) {
3440  /* special case for unison puncta inclinata */
3441  fprintf(f, "\\GreEndOfGlyph{23}%%\n");
3442  } else {
3443  fprintf(f, "\\GreEndOfGlyph{9}%%\n");
3444  }
3445  } else if (glyph->u.notes.glyph_type != G_ALTERATION
3446  || !glyph->next) {
3447  fprintf(f, "\\GreEndOfGlyph{0}%%\n");
3448  }
3449  }
3450  break;
3451  }
3452  }
3453  }
3454  return note_unit_count;
3455 }
3456 
3457 static void write_fixed_text_styles(FILE *f, gregorio_character *syllable_text,
3458  gregorio_character *next_syllable_text)
3459 {
3460  if (syllable_text) {
3463  fprintf(f, "\\GreSetFixedTextFormat{%d}",
3466  }
3467  }
3468  if (next_syllable_text) {
3470  next_syllable_text);
3472  fprintf(f, "\\GreSetFixedNextTextFormat{%d}",
3475  }
3476  }
3477 }
3478 
3479 static void write_text(FILE *const f, const gregorio_character *const text)
3480 {
3481  if (text == NULL) {
3482  fprintf(f, "{}{}{}{}{}");
3483  return;
3484  }
3485  fprintf(f, "{");
3489  fprintf(f, "}{");
3495  fprintf(f, "}");
3496 }
3497 
3498 /*
3499  * Function printing the line clef change (only updating \localleftbox, not
3500  * printing the key). Useful for \GreDiscretionary.
3501  * TODO: I'm not sure about the third argument, but that's how it's called in
3502  * \GreChangeClef.
3503  */
3505  const gregorio_element *const current_element)
3506 {
3507  if (current_element->type == GRE_CLEF) {
3508  /* the third argument is 0 or 1 according to the need for a space
3509  * before the clef */
3510  fprintf(f, "\\GreSetLinesClef{%c}{%d}{1}{%d}{%c}{%d}{%d}%%\n",
3522  }
3523 }
3524 
3525 static __inline bool next_is_bar(const gregorio_syllable *syllable,
3526  const gregorio_element *element)
3527 {
3528  bool got_custos = false;
3529  if (element) {
3530  element = element->next;
3531  }
3532 
3533  while (syllable) {
3534  if (element) {
3535  if (element->type == GRE_BAR) {
3536  return true;
3537  }
3538  /* allow no more than one manual custos before a bar */
3539  if (got_custos || !is_manual_custos(element)) {
3540  return false;
3541  }
3542  got_custos = true;
3543  if (element->next) {
3544  /* look at the next element */
3545  element = element->next;
3546  continue;
3547  }
3548  }
3549 
3550  syllable = syllable->next_syllable;
3551  if (!syllable) {
3552  return false;
3553  }
3554 
3555  element = syllable->elements[0];
3556  }
3557 
3558  /* not reachable unless there's a programming error */
3559  /* LCOV_EXCL_START */
3560  gregorio_fail(next_is_bar, "unexpected end of syllables/elements");
3561  return false;
3562  /* LCOV_EXCL_STOP */
3563 }
3564 
3566  /* Very last, if the syllable is the end of a no-linebreak area: */
3567  if (syllable->no_linebreak_area == NLBA_END) {
3568  fprintf(f, "\\GreEndNLBArea{%d}{0}%%\n",
3569  next_is_bar(syllable, NULL)? 3 : 1);
3570  }
3571  if (syllable->euouae == EUOUAE_END) {
3572  fprintf(f, "\\GreEndEUOUAE{%d}%%\n",
3573  next_is_bar(syllable, NULL)? 3 : 1);
3574  }
3575 }
3576 
3578 {
3580  fprintf(f, "\\GreFinal%s{%%\n", type);
3581  /* first element will be the bar, which we just handled, so skip it */
3582  for (element = (*syllable->elements)->next; element;
3583  element = element->next) {
3584  switch (element->type) {
3585  case GRE_TEXVERB_ELEMENT:
3586  if (element->texverb) {
3587  fprintf(f, "%% verbatim text at element level:\n%s%%\n"
3588  "%% end of verbatim text\n",
3589  gregorio_texverb(element->texverb));
3590  }
3591  break;
3592 
3593  case GRE_CUSTOS:
3594  assert(element->u.misc.pitched.force_pitch);
3595  fprintf(f, "\\GreFinalCustos{%d}{}%%\n",
3596  pitch_value(element->u.misc.pitched.pitch));
3597  break;
3598 
3599  case GRE_END_OF_LINE:
3600  fprintf(f, "\\GreFinalNewLine%%\n");
3601  break;
3602 
3603  default:
3604  /* do nothing */
3605  break;
3606  }
3607  }
3608  fprintf(f, "}%%\n");
3610 }
3611 
3612 static __inline void write_syllable_point_and_click(FILE *const f,
3613  const gregorio_syllable *const syllable,
3614  const gregoriotex_status *const status)
3615 {
3616  if (status->point_and_click && syllable->src_line) {
3617  fprintf(f, "%u:%u:%u", syllable->src_line, syllable->src_offset,
3618  syllable->src_column + 1);
3619  }
3620 }
3621 
3622 static void write_syllable_text(FILE *f, const char *const syllable_type,
3623  const gregorio_syllable *const syllable,
3624  const bool ignored __attribute__((unused)))
3625 {
3626  if (syllable_type != NULL) {
3627  fprintf(f, "%s{%s%s\\GreSetThisSyllable", syllable_type,
3628  syllable->clear? "\\GreClearSyllableText" : "",
3629  syllable->forced_center? "\\GreGABCForceCenters" : "");
3630  write_text(f, syllable->text);
3631  fprintf(f, "}");
3632  }
3633 }
3634 
3635 static void write_first_syllable_text(FILE *f, const char *const syllable_type,
3636  const gregorio_syllable *const syllable,
3637  const bool end_of_word)
3638 {
3639  const gregorio_character *const text = syllable->text;
3640  gregorio_not_null(syllable_type, write_first_syllable_text, return);
3641  if (text == NULL) {
3642  fprintf(f, "}{%s}{\\GreSetNoFirstSyllableText}", syllable_type);
3643  } else {
3644  gregorio_character *text_with_initial = gregorio_clone_characters(text),
3645  *text_without_initial = gregorio_clone_characters(text);
3646  const gregorio_character *t;
3647 
3648  /* find out if there is a forced center -> has_forced_center */
3651 
3652  gregorio_rebuild_first_syllable(&text_with_initial, false);
3653  gregorio_rebuild_characters(&text_with_initial, center, false);
3654  gregorio_set_first_word(&text_with_initial);
3655 
3656  gregorio_rebuild_first_syllable(&text_without_initial, true);
3657  gregorio_rebuild_characters(&text_without_initial, center, true);
3658  gregorio_set_first_word(&text_without_initial);
3659 
3660  fprintf(f, "}{%s}{%s%s\\GreSetFirstSyllableText", syllable_type,
3661  syllable->clear? "\\GreClearSyllableText" : "",
3662  syllable->forced_center? "\\GreGABCForceCenters" : "");
3663 
3664  fprintf(f, "{");
3666  text_with_initial, f, &gtex_write_verb, &gtex_print_char,
3668  fprintf(f, "}{{");
3669  gregorio_write_text(WTP_FIRST_SYLLABLE, text_without_initial, f,
3672  fprintf(f, "}}{{");
3673  gregorio_write_text(WTP_NORMAL, text_with_initial, f, &gtex_write_verb,
3678  fprintf(f, "}}");
3679 
3680  /* Check to see if we need to force a hyphen (empty first syllable) */
3681  for (t = text_without_initial; t; t = t->next_character) {
3682  if (t->is_character) {
3683  break;
3684  } else if (t->cos.s.type == ST_T_BEGIN) {
3685  if (t->cos.s.style == ST_VERBATIM ||
3686  t->cos.s.style == ST_SPECIAL_CHAR) {
3687  break;
3688  } else if (t->cos.s.style == ST_INITIAL) {
3689  for (; t; t = t->next_character) {
3690  if (!t->is_character && t->cos.s.type == ST_T_END
3691  && t->cos.s.style == ST_INITIAL) {
3692  break;
3693  }
3694  }
3695  if (!t) break;
3696  }
3697  }
3698  }
3699  if (t || end_of_word) {
3700  fprintf(f, "{}");
3701  } else {
3702  fprintf(f, "{\\GreEmptyFirstSyllableHyphen}");
3703  }
3704 
3705  fprintf(f, "}");
3706 
3707  gregorio_free_characters(text_with_initial);
3708  gregorio_free_characters(text_without_initial);
3709  }
3710 }
3711 
3712 static __inline void scan_syllable_for_eol(
3713  const gregorio_syllable *const syllable, char *const eol_forces_custos)
3714 {
3715  const gregorio_element *element;
3716  if (syllable->elements) {
3717  for (element = *(syllable->elements); element; element = element->next) {
3718  if (element->type == GRE_END_OF_LINE) {
3719  if (element->u.misc.unpitched.info.eol_forces_custos) {
3720  *eol_forces_custos = element->u.misc.unpitched.info
3721  .eol_forces_custos_on? '1' : '0';
3722  }
3723  }
3724  }
3725  }
3726 }
3727 
3728 /*
3729  * euouae_follows will be
3730  * - '\0' if no euouae follows
3731  * - '0' if euouae follows with no intervening linebreak
3732  * - '1' if euouae follows with an intervening linebreak
3733  *
3734  * eol_forces_custos will be
3735  * - '\0' if no linebreak follows or doesn't force a custos
3736  * - '0' if a linebreak follows and forces custos off
3737  * - '1' if a linebreak follows and forces custos on
3738  */
3740  char *const euouae_follows, char *const eol_forces_custos,
3741  unsigned short *const next_euouae_id)
3742 {
3743  bool has_intervening_linebreak = false;
3744 
3745  *euouae_follows = '\0';
3746  *eol_forces_custos = '\0';
3747  *next_euouae_id = 0;
3748 
3749  if (syllable->next_syllable) {
3750  for (syllable = syllable->next_syllable;
3751  syllable && syllable->elements && *(syllable->elements)
3752  && (*(syllable->elements))->type == GRE_END_OF_LINE;
3753  syllable = syllable->next_syllable) {
3754  has_intervening_linebreak = true;
3755  /* we are at an end-of-line, so check if custos is forced */
3756  scan_syllable_for_eol(syllable, eol_forces_custos);
3757  }
3758  if (syllable) {
3759  scan_syllable_for_eol(syllable, eol_forces_custos);
3760 
3761  if (syllable->euouae == EUOUAE_BEGINNING) {
3762  *next_euouae_id = syllable->euouae_id = ++tex_position_id;
3763  *euouae_follows = has_intervening_linebreak? '1' : '0';
3764  }
3765  }
3766  }
3767 }
3768 
3769 static __inline void write_anticipated_event(FILE *f, const char euouae_follows,
3770  const char eol_forces_custos, const short next_euouae_id)
3771 {
3772  if (euouae_follows) {
3773  fprintf(f, "%%\n\\GreNextSyllableBeginsEUOUAE{%hu}{%c}%%\n",
3774  next_euouae_id, euouae_follows);
3775  }
3776  if (eol_forces_custos) {
3777  fprintf(f, "%%\n\\GreUpcomingNewLineForcesCustos{%c}%%\n",
3778  eol_forces_custos);
3779  }
3780 }
3781 
3782 static __inline unsigned int count_note_units(const gregorio_element *element)
3783 {
3784  unsigned int count = 0;
3785  for (; element; element = element->next) {
3786  if (element->type == GRE_ELEMENT) {
3787  const gregorio_glyph *glyph;
3788  for (glyph = element->u.first_glyph; glyph; glyph = glyph->next) {
3789  if (glyph->type == GRE_GLYPH) {
3791  }
3792  }
3793  }
3794  }
3795  return count;
3796 }
3797 
3798 static __inline void handle_last_of_voice(FILE *const f,
3799  const gregorio_syllable *syllable,
3800  const gregorio_element *const element,
3801  const gregorio_element *const last_of_voice)
3802 {
3803  if (element == last_of_voice) {
3804  if (syllable->next_syllable) {
3805  /* check for no-element syllables that follow */
3806  for (syllable = syllable->next_syllable;
3807  syllable && (!syllable->elements || !*(syllable->elements));
3808  syllable = syllable->next_syllable) {
3809  /* just loop */
3810  }
3811  /* if syllable is NULL here, then all syllables that follow
3812  * have no elements */
3813  }
3814  /* emit GreLastOfScore if we are either on the last syllable (and
3815  * thus the loop above is not executed, leaving syllable at the
3816  * current syllable) or if a syllable which follows the current
3817  * syllable has an element of some sort (and thus the loop above
3818  * stopped before running out of syllables); in any case, the check
3819  * is that syllable, at this point, is not NULL */
3820  if (syllable) {
3821  fprintf(f, "\\GreLastOfScore");
3822  }
3823  }
3824 }
3825 
3827  const gregorio_element *element)
3828 {
3829  if (element) {
3830  element = element->next;
3831  }
3832 
3833  while (syllable) {
3834  if (element) {
3835  if (element->type == GRE_END_OF_LINE) {
3836  return true;
3837  }
3838  break;
3839  }
3840 
3841  syllable = syllable->next_syllable;
3842  if (syllable) {
3843  element = syllable->elements[0];
3844  }
3845  }
3846 
3847  return false;
3848 }
3849 
3850 /*
3851  * Arguments are relatively obvious. The most obscure is certainly first_of_disc
3852  * which is 0 all the time, except in the case of a "clef change syllable". In
3853  * this case we make a \GreDiscretionary with two arguments:
3854  * 1.what should be printed if the syllable is the last of its line (which
3855  * basically means everything but clefs and custos), and
3856  * 2. what should be printed if it's in a middle of a line (which means
3857  * everything)
3858  * So the first_of_disc argument is:
3859  * 0 if we don't know (general case)
3860  * 1 in case of the first argument of a \GreDiscretionary
3861  * 2 if we are in the second argument (necessary in order to avoid infinite loops)
3862  */
3864  unsigned char first_of_disc, gregoriotex_status *const status,
3865  const gregorio_score *const score,
3866  const gregorio_element *const *const last_of_voice,
3867  void (*const write_this_syllable_text)
3868  (FILE *, const char *, const gregorio_syllable *, bool))
3869 {
3870  const gregorio_element *clef_change_element = NULL, *element;
3871  const char *syllable_type = NULL;
3872  bool anticipated_event_written = false;
3873  bool end_of_word;
3874  bool end_of_line;
3875  char euouae_follows;
3876  char eol_forces_custos;
3877  unsigned short next_euouae_id;
3878  unsigned int note_unit_count;
3880  gtex_alteration alteration = ALT_NONE;
3881 
3883  end_of_word = syllable->position == WORD_END
3884  || syllable->position == WORD_ONE_SYLLABLE || !syllable->text
3885  || !syllable->next_syllable;
3886  /* Very first: before anything, if the syllable is the beginning of a
3887  * no-linebreak area: */
3888  if (syllable->no_linebreak_area == NLBA_BEGINNING) {
3889  fprintf(f, "\\GreBeginNLBArea{1}{0}%%\n");
3890  }
3891  if (syllable->euouae == EUOUAE_BEGINNING) {
3892  fprintf(f, "\\GreBeginEUOUAE{%hu}%%\n", syllable->euouae_id);
3893  }
3894  if (syllable->elements && *(syllable->elements)) {
3895  /*
3896  * first we check if the syllable is only a end of line. If it is the
3897  * case, we don't print anything but a comment (to be able to read it
3898  * if we read GregorioTeX). The end of lines are treated separately in
3899  * GregorioTeX, it is buit inside the TeX structure.
3900  */
3901  if ((syllable->elements)[0]->type == GRE_END_OF_LINE) {
3902  gregorio_assert(syllable != score->first_syllable, write_syllable,
3903  "line break is not supported on the first syllable",
3904  return);
3905  if ((syllable->elements)[0]->u.misc.unpitched.info.eol_ragged) {
3906  fprintf(f, "%%\n%%\n\\GreNewParLine %%\n%%\n%%\n");
3907  } else {
3908  fprintf(f, "%%\n%%\n\\GreNewLine %%\n%%\n%%\n");
3909  }
3910  write_this_syllable_text(f, NULL, syllable, end_of_word);
3911  return;
3912  }
3913  /*
3914  * This case is not simple: if the syllable contains a clef change,
3915  * whether it is (c4) or (::c4) or (z0::c4), we put it in a
3916  * discretionary. Warning: only these three cases will have the expected
3917  * effect. So first we detect it:
3918  */
3919  if (first_of_disc == 0) { /* to avoid infinite loops */
3920  clef_change_element = gregorio_get_clef_change(syllable);
3921  if (clef_change_element) {
3922  gregorio_assert(syllable != score->first_syllable,
3924  "clef change is not supported on the first syllable",
3925  return);
3926  /*
3927  * In this case, the first thing to do is to change the line clef
3928  */
3929  gregoriotex_print_change_line_clef(f, clef_change_element);
3930  fprintf(f, "\\GreDiscretionary{0}{%%\n");
3931  write_syllable(f, syllable, 1, status, score, last_of_voice,
3933  fprintf(f, "}{%%\n");
3934  write_syllable(f, syllable, 2, status, score, last_of_voice,
3936  fprintf(f, "}%%\n");
3937  write_this_syllable_text(f, NULL, syllable, end_of_word);
3938  return;
3939  }
3940  }
3942  syllable->next_syllable? syllable->next_syllable->text : NULL);
3943  if ((syllable->elements)[0]->type == GRE_BAR) {
3944  if (syllable != score->first_syllable) {
3945  if (!syllable->next_syllable && !syllable->text
3946  && (syllable->elements)[0]->u.misc.unpitched.info.bar
3947  == B_DIVISIO_FINALIS) {
3948  handle_final_bar(f, "DivisioFinalis", syllable);
3949  write_this_syllable_text(f, NULL, syllable, end_of_word);
3950  return;
3951  }
3952  if (!syllable->next_syllable && !syllable->text
3953  && (syllable->elements)[0]->u.misc.unpitched.info.bar
3954  == B_DIVISIO_MAIOR) {
3955  handle_final_bar(f, "DivisioMaior", syllable);
3956  write_this_syllable_text(f, NULL, syllable, end_of_word);
3957  return;
3958  }
3959  }
3960  /* the special case of first syllable will be treated as a normal
3961  * bar syllable */
3962  syllable_type = "\\GreBarSyllable";
3963  } else {
3964  if (first_of_disc != 0) {
3965  syllable_type = "\\GreBarSyllable";
3966  } else {
3967  syllable_type = "\\GreSyllable";
3968  }
3969  }
3970  } else {
3972  syllable->next_syllable? syllable->next_syllable->text : NULL);
3973  syllable_type = "\\GreNoNoteSyllable";
3974  }
3975  write_this_syllable_text(f, syllable_type, syllable, end_of_word);
3976  fprintf(f, "{}{\\Gre%s}", syllable->first_word ? "FirstWord" : "Unstyled");
3977  if (end_of_word) {
3978  fprintf(f, "{1}");
3979  } else {
3980  fprintf(f, "{0}");
3981  }
3983  anticipate_event(syllable, &euouae_follows, &eol_forces_custos,
3984  &next_euouae_id);
3985  if (syllable->next_syllable) {
3986  fprintf(f, "{%s\\GreSetNextSyllable",
3987  syllable->next_syllable->forced_center?
3988  "\\GreGABCNextForceCenters" : "");
3989  write_text(f, syllable->next_syllable->text);
3990  if (end_of_line || first_of_disc == 1) {
3991  fprintf(f, "\\GreLastOfLine");
3992  } else if (euouae_follows) {
3993  fprintf(f, "\\GreLastSyllableBeforeEUOUAE{%hu}{%c}",
3994  next_euouae_id, euouae_follows);
3995  }
3996  fprintf(f, "}{");
3998  syllable_first_type(syllable->next_syllable, &alignment, &alteration);
3999  fprintf(f, "}{{%d}{%d}}{", alignment, alteration);
4000  } else {
4001  fprintf(f, "{\\GreSetNextSyllable{}{}{}{}{}");
4002  if (end_of_line || first_of_disc == 1) {
4003  fprintf(f, "\\GreLastOfLine");
4004  }
4005  fprintf(f, "}{");
4007  fprintf(f, "}{{%d}{%d}}{", AT_EMPTY_SYLLABLE, ALT_NONE);
4008  }
4009  if (syllable->translation) {
4010  if (syllable->translation_type == TR_WITH_CENTER_BEGINNING) {
4011  fprintf(f, "%%\n\\GreWriteTranslationWithCenterBeginning{");
4012  } else {
4013  fprintf(f, "%%\n\\GreWriteTranslation{");
4014  }
4015  write_translation(f, syllable->translation);
4016  fprintf(f, "}%%\n");
4017  }
4018  if (syllable->translation_type) {
4019  if (syllable->translation_type == TR_WITH_CENTER_END)
4020  fprintf(f, "%%\n\\GreTranslationCenterEnd %%\n");
4021  }
4022  if (syllable->abovelinestext) {
4023  fprintf(f, "%%\n\\GreSetTextAboveLines{%s}%%\n",
4024  syllable->abovelinestext);
4025  }
4026  fprintf(f, "}{%%\n");
4027 
4028  fprintf(f, "\\GreSyllableNoteCount{%u}%%\n", syllable->elements?
4029  count_note_units(*syllable->elements) : 0);
4030 
4031  note_unit_count = 0;
4032  if (syllable->elements) {
4033  for (element = *syllable->elements; element; element = element->next) {
4034  if (element->nabc_lines && element->nabc) {
4035  signed char high_pitch = UNDETERMINED_HEIGHT;
4036  signed char low_pitch = UNDETERMINED_HEIGHT;
4037  size_t i;
4039  &low_pitch);
4040  fixup_height_extrema(&high_pitch, &low_pitch);
4041  for (i = 0; i < element->nabc_lines; i++) {
4042  if (element->nabc[i]) {
4043  fprintf(f, "\\GreNABCNeumes{%d}{", (int)(i+1));
4044  tex_escape_text(f, element->nabc[i]);
4045  fprintf(f, "}{%d}{%d}%%\n", pitch_value(high_pitch),
4046  pitch_value(low_pitch));
4047  }
4048  }
4049  }
4050  switch (element->type) {
4051  case GRE_SPACE:
4052  switch (element->u.misc.unpitched.info.space) {
4053  case SP_LARGER_SPACE:
4054  fprintf(f, "\\GreEndOfElement{1}{0}{%u}%%\n",
4055  note_unit_count);
4056  break;
4057  case SP_GLYPH_SPACE:
4058  fprintf(f, "\\GreEndOfElement{2}{0}{%u}%%\n",
4059  note_unit_count);
4060  break;
4061  case SP_NEUMATIC_CUT:
4062  fprintf(f, "\\GreEndOfElement{0}{0}{%u}%%\n",
4063  note_unit_count);
4064  break;
4065  case SP_AD_HOC_SPACE:
4066  fprintf(f, "\\GreAdHocSpaceEndOfElement{%s}{0}{%u}%%\n",
4067  element->u.misc.unpitched.info.ad_hoc_space_factor,
4068  note_unit_count);
4069  break;
4070  case SP_GLYPH_SPACE_NB:
4071  fprintf(f, "\\GreEndOfElement{2}{1}{%u}%%\n",
4072  note_unit_count);
4073  break;
4074  case SP_LARGER_SPACE_NB:
4075  fprintf(f, "\\GreEndOfElement{1}{1}{%u}%%\n",
4076  note_unit_count);
4077  break;
4078  case SP_NEUMATIC_CUT_NB:
4079  fprintf(f, "\\GreEndOfElement{0}{1}{%u}%%\n",
4080  note_unit_count);
4081  break;
4082  case SP_AD_HOC_SPACE_NB:
4083  fprintf(f, "\\GreAdHocSpaceEndOfElement{%s}{1}{%u}%%\n",
4084  element->u.misc.unpitched.info.ad_hoc_space_factor,
4085  note_unit_count);
4086  break;
4087  default:
4088  /* not reachable unless there's a programming error */
4089  /* LCOV_EXCL_START */
4091  "encountered an unexpected element-level space");
4092  break;
4093  /* LCOV_EXCL_STOP */
4094  }
4095  break;
4096 
4097  case GRE_TEXVERB_ELEMENT:
4098  if (element->texverb) {
4099  fprintf(f, "%% verbatim text at element level:\n%s%%\n"
4100  "%% end of verbatim text\n",
4101  gregorio_texverb(element->texverb));
4102  }
4103  break;
4104 
4105  case GRE_NLBA:
4106  if (element->u.misc.unpitched.info.nlba == NLBA_BEGINNING) {
4107  fprintf(f, "\\GreBeginNLBArea{0}{0}%%\n");
4108  } else {
4109  fprintf(f, "\\GreEndNLBArea{%d}{0}%%\n",
4110  next_is_bar(syllable, element)? 3 : 0);
4111  }
4112  break;
4113 
4114  case GRE_ALT:
4115  if (element->texverb) {
4116  fprintf(f, "\\GreSetTextAboveLines{%s}%%\n",
4117  gregorio_texverb(element->texverb));
4118  }
4119  break;
4120 
4121  case GRE_CLEF:
4122  /* We don't print clef changes at the end of a line */
4123  if (first_of_disc != 1) {
4125  signed char next_note_pitch;
4126  gregorio_shape next_note_alteration;
4127 
4128  next_note_pitch = gregorio_adjust_pitch_into_staff(
4130  syllable, element, NULL, &next_note_alteration)
4131  - element->u.misc.clef.pitch_difference);
4132 
4133  fputs(next_custos(next_note_pitch, next_note_alteration,
4134  status), f);
4136  } else {
4137  /* the third argument is 0 or 1 according to the need
4138  * for a space before the clef */
4139  fprintf(f, "\\GreChangeClef{%c}{%d}{%c}{%d}{%c}{%d}{%d}%%\n",
4140  gregorio_clef_to_char(element->u.misc.clef.clef),
4141  element->u.misc.clef.line,
4142  (!element->previous || element->previous->type
4143  == GRE_BAR)? '0' : '1',
4144  clef_flat_height(element->u.misc.clef.clef,
4145  element->u.misc.clef.line,
4146  element->u.misc.clef.flatted),
4148  element->u.misc.clef.secondary_clef),
4149  element->u.misc.clef.secondary_line,
4151  element->u.misc.clef.secondary_clef,
4152  element->u.misc.clef.secondary_line,
4153  element->u.misc.clef.secondary_flatted));
4154  }
4155  }
4156  break;
4157 
4158  case GRE_CUSTOS:
4159  if (first_of_disc != 1) {
4160  signed char next_note_pitch;
4161  gregorio_shape next_note_alteration;
4162  const char *alteration = "";
4163  /* We don't print custos before a bar at the end of a line.
4164  * We also print an unbreakable larger space before the
4165  * custos */
4166  handle_last_of_voice(f, syllable, element, *last_of_voice);
4167  next_note_pitch = gregorio_determine_next_pitch(syllable,
4168  element, NULL, &next_note_alteration);
4169  if (!element->u.misc.pitched.force_pitch) {
4170  alteration = alteration_name(next_note_alteration);
4171  }
4172  fprintf(f, "\\GreCustos{%d}{%s}%s%%\n",
4173  pitch_value(element->u.misc.pitched.pitch), alteration,
4174  next_custos(next_note_pitch, next_note_alteration,
4175  status));
4176  ++note_unit_count;
4177  }
4178  break;
4179 
4180  case GRE_SUPPRESS_CUSTOS:
4181  handle_last_of_voice(f, syllable, element, *last_of_voice);
4182  fprintf(f, "\\GreSuppressEolCustos %%\n");
4183  status->suppressed_custos = true;
4184  break;
4185 
4186  case GRE_BAR:
4187  handle_last_of_voice(f, syllable, element, *last_of_voice);
4188  write_bar(f, score, syllable, element, first_of_disc);
4189  break;
4190 
4191  case GRE_END_OF_LINE:
4192  if (!element->next) {
4193  write_anticipated_event(f, euouae_follows,
4194  eol_forces_custos, next_euouae_id);
4195  anticipated_event_written = true;
4196  }
4197  /* here we suppose we don't have two linebreaks in the same
4198  * syllable */
4199  if (element->u.misc.unpitched.info.eol_ragged) {
4200  fprintf(f, "%%\n%%\n\\GreNewParLine %%\n%%\n%%\n");
4201  } else {
4202  fprintf(f, "%%\n%%\n\\GreNewLine %%\n%%\n%%\n");
4203  }
4204  break;
4205 
4206  default:
4207  /* here current_element->type is GRE_ELEMENT */
4208  assert(element->type == GRE_ELEMENT);
4209  handle_last_of_voice(f, syllable, element, *last_of_voice);
4210  note_unit_count += write_element(f, syllable, element, status,
4211  score);
4212  if (element->next && (element->next->type == GRE_ELEMENT
4213  || (element->next->next
4214  && element->next->type == GRE_ALT
4215  && element->next->next->type ==
4216  GRE_ELEMENT))) {
4217  fprintf(f, "\\GreEndOfElement{0}{0}{%u}%%\n",
4218  note_unit_count);
4219  }
4220  break;
4221  }
4222  }
4223  }
4224  if (!anticipated_event_written) {
4225  write_anticipated_event(f, euouae_follows, eol_forces_custos,
4226  next_euouae_id);
4227  }
4228  fprintf(f, "}%%\n");
4229  if (syllable->position == WORD_END
4230  || syllable->position == WORD_ONE_SYLLABLE || !syllable->text) {
4231  fprintf(f, "%%\n");
4232  }
4234 }
4235 
4236 static char *digest_to_hex(const unsigned char digest[SHA1_DIGEST_SIZE])
4237 {
4238  static const char *const hex = "0123456789abcdef";
4239  static char result[41];
4240 
4241  char *p = result;
4242  unsigned char byte;
4243 
4244  int i;
4245  for (i = 0; i < SHA1_DIGEST_SIZE; ++i) {
4246  byte = digest[i];
4247 
4248  *(p++) = hex[(byte >> 4) & 0x0FU];
4249  *(p++) = hex[byte & 0x0FU];
4250  }
4251 
4252  *p = '\0';
4253 
4254  return result;
4255 }
4256 
4258  gregorio_score *score, const bool point_and_click,
4259  const gregorio_element **const last_of_voice)
4260 {
4262 
4263  status->bottom_line = false;
4264  status->top_height = status->bottom_height = UNDETERMINED_HEIGHT;
4265  status->abovelinestext = status->translation = false;
4266  status->suppressed_custos = false;
4267 
4268  /* first pass to compute positioning */
4269  for (syllable = score->first_syllable; syllable;
4270  syllable = syllable->next_syllable) {
4271  int voice;
4272 
4273  for (voice = 0; voice < score->number_of_voices; ++voice) {
4275  }
4276  }
4277 
4279 
4280  for (syllable = score->first_syllable; syllable;
4281  syllable = syllable->next_syllable) {
4282  int voice;
4283 
4284  if (syllable->translation) {
4285  status->translation = true;
4286  }
4287 
4288  if (syllable->abovelinestext) {
4289  status->abovelinestext = true;
4290  }
4291 
4292  /* simultaneously compute height extrema and determine the last "real"
4293  * element in each voice */
4294  for (voice = 0; voice < score->number_of_voices; ++voice) {
4296 
4297  for (element = syllable->elements[voice]; element;
4298  element = element->next) {
4300 
4301  switch (element->type) {
4302  case GRE_ALT:
4303  status->abovelinestext = true;
4304  break;
4305 
4306  case GRE_CUSTOS:
4307  last_of_voice[voice] = element;
4308  break;
4309 
4310  case GRE_ELEMENT:
4311  for (glyph = element->u.first_glyph; glyph;
4312  glyph = glyph->next) {
4313  if (glyph->type == GRE_GLYPH) {
4314  last_of_voice[voice] = element;
4316  glyph->u.notes.first_note,
4317  &(status->top_height),
4318  &(status->bottom_height));
4319  }
4320  }
4321  break;
4322 
4323  case GRE_BAR:
4324  last_of_voice[voice] = element;
4325  break;
4326 
4327  default:
4328  /* to eliminate the warning */
4329  break;
4330  }
4331  }
4332  }
4333  }
4334 
4335  fixup_height_extrema(&(status->top_height), &(status->bottom_height));
4336 
4337  status->point_and_click = point_and_click;
4338 }
4339 
4340 static void write_header(FILE *const f, const char *const name,
4341  const char *const value)
4342 {
4343  if (value) {
4344  fprintf(f, "\\GreHeader{");
4346  fprintf(f, "}{");
4348  fprintf(f, "}%%\n");
4349  }
4350 }
4351 
4352 static void write_headers(FILE *const f, gregorio_score *const score)
4353 {
4355 
4356  fprintf(f, "\\GreBeginHeaders %%\n");
4357  for (header = score->headers; header; header = header->next) {
4358  write_header(f, header->name, header->value);
4359  }
4360  fprintf(f, "\\GreEndHeaders %%\n");
4361 }
4362 
4363 static void suppress_expansion(FILE *const f, const char *text)
4364 {
4365  if (!text) {
4366  return;
4367  }
4368 
4369  for (; *text; ++text) {
4370  if (*text == '\\') {
4371  fprintf(f, "\\noexpand");
4372  }
4373  fputc(*text, f);
4374  }
4375 }
4376 
4377 static int first_note_near_clef(const gregorio_score *const score) {
4379  if (score->first_voice_info) {
4380  clef = score->first_voice_info->initial_clef;
4381  if (!clef.secondary_line && !clef.flatted && score->first_syllable
4382  && score->first_syllable->elements) {
4383  const gregorio_element *element = score->first_syllable->elements[0];
4384  if (element && element->type == GRE_ELEMENT) {
4385  const gregorio_glyph *glyph = element->u.first_glyph;
4386  if (glyph && glyph->type == GRE_GLYPH) {
4387  const signed char clef_pitch = LOW_LINE_PITCH
4388  + ((clef.line - 1) * 2);
4389  const gregorio_note *low_note = glyph->u.notes.first_note;
4390  const gregorio_note *high_note = low_note;
4391  switch (glyph->u.notes.glyph_type) {
4392  case G_PODATUS:
4393  /* next note is above the previous */
4394  if (low_note->next) {
4395  high_note = low_note->next;
4396  }
4397  break;
4398  case G_FLEXA:
4399  case G_PORRECTUS:
4400  case G_PORRECTUS_FLEXUS:
4401  /* there is a stem the size