diff --git a/.editorconfig b/.editorconfig
index 8154a87f..61338ead 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -8,5 +8,5 @@ indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
-[*.{py,c,h}]
+[*.{py,c,h,l,y}]
indent_size = 4
diff --git a/doc/Gabc.tex b/doc/Gabc.tex
index 32189a94..e56f91de 100644
--- a/doc/Gabc.tex
+++ b/doc/Gabc.tex
@@ -647,6 +647,19 @@ \subsubsection{Neume Spacing}\label{neumespacing}
space, makes the space that follows a non-breaking space \\
\end{tabularx}
+\subsubsection{Shape Hints}
+
+In some cases, an alternate form of a shape is desired. To do this, put a
+\texttt{[shape:}\textit{hint}\texttt{]} after the figure that needs to be
+altered. The available alternate form hints are as follows:
+
+\begin{tabularx}{\textwidth}{l|X}
+ \textit{hint} & Description \\
+ \hline
+ \texttt{stroke} & Render a clivis/flexus as a stroke (like in a porrectus)
+ rather than as two notes \\
+\end{tabularx}
+
\subsubsection{Additional Symbols}
Puncta mora, episemata, and other symbols may also be added to a note by
diff --git a/src/dump/dump.c b/src/dump/dump.c
index 77fcfeea..20faa9b1 100644
--- a/src/dump/dump.c
+++ b/src/dump/dump.c
@@ -5,19 +5,19 @@
* Copyright (C) 2007-2021 The Gregorio Project (see CONTRIBUTORS.md)
*
* This file is part of Gregorio.
- *
+ *
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
- *
+ *
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
*/
#include "config.h"
@@ -424,9 +424,13 @@ void dump_write_score(FILE *f, gregorio_score *score)
gregorio_texverb(note->texverb));
}
if (note->choral_sign) {
- fprintf(f, " Choral Sign \"%s\"\n",
+ fprintf(f, " choral sign \"%s\"\n",
note->choral_sign);
}
+ if (note->shape_hint) {
+ fprintf(f, " shape hint \"%s\"\n",
+ note->shape_hint);
+ }
if (note->signs) {
fprintf(f, " signs %d (%s)\n",
note->signs,
diff --git a/src/gabc/gabc-notes-determination.l b/src/gabc/gabc-notes-determination.l
index d142b546..9fc185f3 100644
--- a/src/gabc/gabc-notes-determination.l
+++ b/src/gabc/gabc-notes-determination.l
@@ -778,6 +778,7 @@ void gabc_det_notes_finish(void)
%x overledger overledger2
%x underledger underledger2
%x endledger
+%x shapehint
%%
\% {
@@ -1021,11 +1022,11 @@ void gabc_det_notes_finish(void)
gregorio_add_texverb_to_note(current_note, gabc_unescape(tempstr));
}
(\$.|[^\]])+ {
- gregorio_add_cs_to_note(¤t_note,
+ gregorio_add_cs_to_note(current_note,
gabc_unescape(gabc_notes_determination_text), false);
}
(\$.|[^\]])+ {
- gregorio_add_cs_to_note(¤t_note,
+ gregorio_add_cs_to_note(current_note,
gabc_unescape(gabc_notes_determination_text), true);
}
(\$.|[^\]])+ {
@@ -1109,7 +1110,14 @@ void gabc_det_notes_finish(void)
add_variable_ledger(SO_UNDER, gabc_notes_determination_text);
BEGIN(endledger);
}
-\] {
+\[shape: {
+ BEGIN(shapehint);
+ }
+(\$.|[^\]])+ {
+ gregorio_add_shape_hint_to_note(current_note,
+ gabc_unescape(gabc_notes_determination_text));
+ }
+\] {
BEGIN(INITIAL);
}
\[[ou]slur:[012];[^,]+,[^\]]+\] {
diff --git a/src/gabc/gabc-score-determination.c b/src/gabc/gabc-score-determination.c
index 31a69c8c..6dd5f6aa 100644
--- a/src/gabc/gabc-score-determination.c
+++ b/src/gabc/gabc-score-determination.c
@@ -6,18 +6,18 @@
* Copyright (C) 2016-2021 The Gregorio Project (see CONTRIBUTORS.md)
*
* This file is part of Gregorio.
- *
+ *
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
- *
+ *
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
- *
- * You should have received a copy of the GNU General Public License along with
+ *
+ * You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*/
@@ -662,8 +662,8 @@ char *gabc_unescape(const char *const string)
{
/*
* in this context, unescape means to discard any special meaning of a
- * character that follows a backslash. Thus backslash-{something} is
- * reduced to {something}
+ * character that follows a dollar sign. Thus $-{something} is reduced
+ * to {something}
*/
char *result, *to;
const char *from = string;
diff --git a/src/gabc/gabc-write.c b/src/gabc/gabc-write.c
index 7e5985de..9ebed376 100644
--- a/src/gabc/gabc-write.c
+++ b/src/gabc/gabc-write.c
@@ -5,20 +5,20 @@
* Copyright (C) 2006-2021 The Gregorio Project (see CONTRIBUTORS.md)
*
* This file is part of Gregorio.
- *
+ *
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
- *
+ *
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
- *
- * You should have received a copy of the GNU General Public License along with
+ *
+ * You should have received a copy of the GNU General Public License along with
* this program. If not, see .
- *
+ *
* This is a simple and easyly understandable output module. If you want to
* write a module, you can consider it as a model.
*/
@@ -64,7 +64,7 @@ static __inline void unsupported(const char *fn, const int line,
/* LCOV_EXCL_STOP */
/*
- * Output one attribute, allowing for multi-line values
+ * Output one attribute, allowing for multi-line values
*/
static void gabc_write_str_attribute(FILE *f, const char *name,
const char *attr)
@@ -75,13 +75,13 @@ static void gabc_write_str_attribute(FILE *f, const char *name,
}
/*
- *
+ *
* Then we start the functions made to write the text of the syllable. See
* comments on struct.h and struct-utils.c to understand more deeply.
- *
+ *
* This first function will be called each time we will encounter a
* gregorio_character which is the beginning of a style.
- *
+ *
*/
static void gabc_write_begin(FILE *f, grestyle_style style)
@@ -133,9 +133,9 @@ static void gabc_write_begin(FILE *f, grestyle_style style)
}
/*
- *
+ *
* This function is about the same but for ends of styles.
- *
+ *
*/
static void gabc_write_end(FILE *f, grestyle_style style)
@@ -189,11 +189,11 @@ static void gabc_write_end(FILE *f, grestyle_style style)
}
/*
- *
+ *
* The function called when we will encounter a character. There may be other
- * representations of the character (for example for Omega), so it is necessary
+ * representations of the character (for example for Omega), so it is necessary
* to have such a function defined in each module.
- *
+ *
*/
static void gabc_print_char(FILE *f, const grewchar to_print)
@@ -263,11 +263,11 @@ static void gabc_print_string(FILE *f, const char *first_char)
}
/*
- *
+ *
* This function writes the special chars. As the specials chars are
- * represented simply in gabc, this function is very simple, but for TeX output
+ * represented simply in gabc, this function is very simple, but for TeX output
* modules, this may be.. a little more difficult.
- *
+ *
*/
static void gabc_write_special_char(FILE *f, const grewchar *first_char)
{
@@ -277,10 +277,10 @@ static void gabc_write_special_char(FILE *f, const grewchar *first_char)
}
/*
- *
+ *
* This functions writes verbatim output... but as the previous one it is very
* simple.
- *
+ *
*/
static void gabc_write_verb(FILE *f, const grewchar *first_char)
{
@@ -295,10 +295,10 @@ static void gabc_write_verb(FILE *f, const grewchar *first_char)
}
/*
- *
+ *
* Quite important: the function that writes the liquescentia. It is called at
* the end of the function that writes one glyph.
- *
+ *
*/
static void gabc_write_end_liquescentia(FILE *f, char liquescentia)
@@ -317,9 +317,9 @@ static void gabc_write_end_liquescentia(FILE *f, char liquescentia)
}
/*
- *
+ *
* The function that writes a key change... quite simple.
- *
+ *
*/
static void gabc_write_clef(FILE *f, gregorio_clef_info clef)
@@ -333,9 +333,9 @@ static void gabc_write_clef(FILE *f, gregorio_clef_info clef)
}
/*
- *
+ *
* The function that writes spaces, called when we encounter one.
- *
+ *
*/
static void gabc_write_space(FILE *f, gregorio_space type, char *factor,
@@ -382,9 +382,9 @@ static void gabc_write_space(FILE *f, gregorio_space type, char *factor,
}
/*
- *
+ *
* A function to write a bar.
- *
+ *
*/
static void gabc_write_bar(FILE *f, gregorio_bar type)
@@ -583,9 +583,9 @@ typedef struct glyph_context {
} glyph_context;
/*
- *
+ *
* The function that writes one gregorio_note.
- *
+ *
*/
static void gabc_write_gregorio_note(FILE *f, gregorio_note *note,
@@ -778,6 +778,12 @@ static void gabc_write_gregorio_note(FILE *f, gregorio_note *note,
gabc_print_string(f, gregorio_texverb(note->texverb));
fprintf(f, "]");
}
+ if (note->choral_sign) {
+ fprintf(f, "[cs:%s]", note->choral_sign);
+ }
+ if (note->shape_hint) {
+ fprintf(f, "[shape:%s]", note->shape_hint);
+ }
}
static void get_next_hepisema_adjustments(unsigned short *adjustment_index,
@@ -916,11 +922,11 @@ static __inline void close_hepisema_adjustment(FILE *const f,
}
/*
- *
+ *
* The function that writes one glyph. If it is really a glyph (meaning not a
* space or an alteration), we just do like always, a loop on the notes and a
* call to the function that writes one note on each of them.
- *
+ *
*/
static void gabc_write_gregorio_glyph(FILE *f, gregorio_glyph *glyph,
@@ -1018,12 +1024,12 @@ static void gabc_write_gregorio_glyph(FILE *f, gregorio_glyph *glyph,
}
/*
- *
- * To write an element, first we check the type of the element (if it is a bar,
+ *
+ * To write an element, first we check the type of the element (if it is a bar,
* etc.), and if it is really an element, we make a loop on the list of glyphs
* inside the neume, and for each of them we call the function that will write
* one glyph.
- *
+ *
*/
static void gabc_write_gregorio_element(FILE *f, gregorio_element *element,
@@ -1115,11 +1121,11 @@ static void gabc_write_gregorio_element(FILE *f, gregorio_element *element,
}
/*
- *
+ *
* Here is defined the function that will write the list of gregorio_elements.
* It is very simple: it makes a loop in which it calls a function that writes
* one element.
- *
+ *
*/
static bool gabc_write_gregorio_elements(FILE *f, gregorio_element *element,
@@ -1146,9 +1152,9 @@ static bool gabc_write_gregorio_elements(FILE *f, gregorio_element *element,
}
/*
- *
+ *
* Here it goes, we are writing a gregorio_syllable.
- *
+ *
*/
static void gabc_write_gregorio_syllable(FILE *f, gregorio_syllable *syllable,
@@ -1208,10 +1214,10 @@ static void gabc_write_gregorio_syllable(FILE *f, gregorio_syllable *syllable,
}
/*
- *
+ *
* This is the top function, the one called when we want to write a
* gregorio_score in gabc.
- *
+ *
*/
void gabc_write_score(FILE *f, gregorio_score *score)
diff --git a/src/gregoriotex/gregoriotex-write.c b/src/gregoriotex/gregoriotex-write.c
index ab913f21..ddf0537e 100644
--- a/src/gregoriotex/gregoriotex-write.c
+++ b/src/gregoriotex/gregoriotex-write.c
@@ -237,6 +237,39 @@ static queuetype queuetype_of(const gregorio_note *const note) {
static grestyle_style gregoriotex_ignore_style = ST_NO_STYLE;
static grestyle_style gregoriotex_next_ignore_style = ST_NO_STYLE;
+static bool glyph_hint(const gregorio_glyph *const glyph,
+ const char *const hint) {
+ const char *const shape_hints = gregorio_glyph_last_note(glyph)->shape_hint;
+ if (shape_hints == NULL) {
+ /* no hints */
+ return false;
+ } else {
+ const char *const found = strstr(shape_hints, hint);
+ if (found == NULL) {
+ /* no match */
+ return false;
+ } else {
+ const char suffix = found[strlen(hint)];
+ if (suffix == '\0' || suffix == ',') {
+ /* one must be true or it's not a match */
+ if (found == shape_hints) {
+ /* found it at the start of shape_hints */
+ return true;
+ } else {
+ /*
+ * we're somewhere after the start, so it's ok to get the
+ * character before; a comma before the start of the found
+ * string is the only remaining way it can be a match
+ */
+ return (*(found - 1) == ',');
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+}
+
/*
* The different liquescentiae are:
* 'Nothing'
@@ -500,6 +533,17 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph,
* for the liquescent forms */
shape = SHAPE_Salicus;
}
+ if (!fuse_to_next_note && (shape == SHAPE_Flexus
+ || shape == SHAPE_FlexusLongqueue
+ || shape == SHAPE_FlexusOpenqueue
+ || shape == SHAPE_FlexusNobar)
+ && liquescentia == LIQ_Nothing
+ && glyph_hint(glyph, "stroke")) {
+ /* "fake" fusion to get the shape we want */
+ fuse_ambitus = 1;
+ liquescentia = "";
+ fuse_tail = FUSE_Up;
+ }
current_note = current_note->next;
if (!current_note->next) {
gregorio_snprintf(buf, BUFSIZE, "%s%s%s%s%s%s", fuse_head, shape,
diff --git a/src/struct.c b/src/struct.c
index 788f9e5b..6871df42 100644
--- a/src/struct.c
+++ b/src/struct.c
@@ -356,7 +356,7 @@ static __inline void change_note_texverb(gregorio_note *note, char *str)
}
}
-void gregorio_add_texverb_to_note(gregorio_note *current_note, char *str)
+void gregorio_add_texverb_to_note(gregorio_note *const current_note, char *str)
{
size_t len;
char *res;
@@ -376,12 +376,26 @@ void gregorio_add_texverb_to_note(gregorio_note *current_note, char *str)
}
}
-void gregorio_add_cs_to_note(gregorio_note *const*const current_note,
+void gregorio_add_cs_to_note(gregorio_note *const current_note,
char *const str, const bool nabc)
{
- if (*current_note) {
- (*current_note)->choral_sign = str;
- (*current_note)->choral_sign_is_nabc = nabc;
+ if (current_note) {
+ if (current_note->choral_sign) {
+ free(current_note->choral_sign);
+ }
+ current_note->choral_sign = str;
+ current_note->choral_sign_is_nabc = nabc;
+ }
+}
+
+void gregorio_add_shape_hint_to_note(gregorio_note *const current_note,
+ char *const str)
+{
+ if (current_note) {
+ if (current_note->shape_hint) {
+ free(current_note->shape_hint);
+ }
+ current_note->shape_hint = str;
}
}
@@ -708,6 +722,7 @@ static __inline void free_one_note(gregorio_note *note)
}
free_one_texverb(note->texverb);
free(note->choral_sign);
+ free(note->shape_hint);
free(note);
}
diff --git a/src/struct.h b/src/struct.h
index 025b23e7..7c1e95e6 100644
--- a/src/struct.h
+++ b/src/struct.h
@@ -456,6 +456,7 @@ typedef struct gregorio_note {
/* choral sign is a letter that appears next to a note in some choral
* scores we put it as char* because sometimes two letters appear */
char *choral_sign;
+ char *shape_hint;
union {
/* note is used for GRE_NOTE */
struct {
@@ -892,8 +893,9 @@ void gregorio_start_autofuse(gregorio_note **current_note,
void gregorio_end_autofuse(gregorio_note **current_note,
const gregorio_scanner_location *loc);
void gregorio_add_texverb_to_note(gregorio_note *current_note, char *str);
-void gregorio_add_cs_to_note(gregorio_note *const*current_note, char *str,
+void gregorio_add_cs_to_note(gregorio_note *current_note, char *str,
bool nabc);
+void gregorio_add_shape_hint_to_note(gregorio_note *current_note, char *str);
void gregorio_add_misc_element(gregorio_element **current_element,
gregorio_type type, gregorio_misc_element_info *info,
unsigned short texverb);