# This is a companion to squarize.py
# coding=utf-8
"""
Companion to squarize.py, building the stem length config
according to various schemas.
Copyright (C) 2016-2025 The Gregorio Project (see CONTRIBUTORS.md)
This file is part of Gregorio.
Gregorio 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.
Gregorio 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 Gregorio. If not, see <
http://www.gnu.org/licenses/>.
This script takes a very simple .sfd file with a few symbols and
builds a complete square notation font. See gregorio-base.sfd for
naming conventions of these symbols.
To build your own font, look at gregorio-base.sfd, and build your
own glyphs from it.
The idea here is to return a "stem schema" describing the lengths
of all possible stems.
Common names of variables:
- font_config(dict): the font configuration (ex: content of
greciliae.json)
- add_suppl(boolean): if we must make queues slightly longer
than their reference height. The height difference is
font_config[font_config['bottom-add']]
- bmu(str): "bottom" or "middle" or "top"
- base (int): font_config['base height']
"""
def get_default_porrectus(font_config, add_suppl, one_bottom):
""" Common subfunction, gets porrectus length.
"""
suppl = font_config['bottom-add'] if add_suppl else 0
return {
"Nothing": {
"1": {
"short": font_config['bottom-porrectus-1'] + suppl,
"long": one_bottom
},
"2": {
"short": font_config['bottom-porrectus-2'] + suppl,
"long": font_config['bottom-porrectus-2'] + suppl
},
"3": {
"short": font_config['bottom-porrectus-3'] + suppl,
"long": font_config['bottom-porrectus-3'] + suppl
},
"4": {
"short": font_config['bottom-porrectus-4'] + suppl,
"long": font_config['bottom-porrectus-4'] + suppl
},
"5": {
"short": font_config['bottom-porrectus-5'] + suppl,
"long": font_config['bottom-porrectus-5'] + suppl
}
}
}
def get_conf(font_config, bmu, suffix, add_suppl, second_suffix=''):
""" Gets a value from font_config, with fallbacks if not present.
- suffix can be 'deminutus', 'oriscus', etc.
- second_suffix can be 'lower' or 'upper', currently handling
quilisma only.
"""
suffixed = bmu+'-'+suffix
second_suffixed = bmu+'-'+suffix+'-'+second_suffix
base = font_config[bmu]
if second_suffixed in font_config:
base = font_config[second_suffixed]
elif suffixed in font_config:
base = font_config[suffixed]
if add_suppl and bmu == 'bottom':
base += font_config['bottom-add']
return base
def get_stem_schema_default(font_config):
""" This returns Gregorio's default schema. This is adapted from
1934 Antiphonale Monasticum (AM), with a few changes:
- values for ancus and salicus (not present in AM)
- more consistent short virga stems. AM has a short and
longer form. See line 6 page 400 for both figures side
to side. The longer form is used.
- ih~ and similar have a slightly shorter queue (AM 400 l. 3)
- small changes for ambitus generally, queues are slightly shorter
- treat similar figures with consistency (gv and ge for instance)
List of some figures in the AM:
- ih(ih) : AM 367, 3 (p. 367, l. 3)
- hg(hg) : AM 367,4
- dc(dc) : AM 366
- ed(ed) : AM 1185,5
- ig(ig) : AM 367,6
- fd(fd) : AM 673,1
- gd(gd) : AM 531,3
- he(he) : AM 420,7
- hd(hd) : AM 1183,1
- ih~(ih~) : AM 396,1
- hg~(hg~) : AM 347,7
- dc~(dc~) : AM 397,4, AM 1182,4
- ed~(ed~) : AM 402,5
- ge~(ge~) : AM 239,4
- gd~(gd~) : AM 239,3
- gc~(gc~) : AM 397,5
"""
base = font_config['base height']
# these are used for both virga and flexus deminutus with ambitus one, for coherence
virga_long = get_conf(font_config, 'bottom', '', False) - 2*base
virga_short = get_conf(font_config, 'middle', '', False) - 2*base
virga_open = get_conf(font_config, 'bottom', '', False) - base
def get_basic(suffix, add_suppl=False, second_suffix=''):
""" Common function for flexus, pes quadratum, pes quassus, etc.
"""
bottom = get_conf(font_config, 'bottom', suffix, add_suppl, second_suffix)
middle = get_conf(font_config, 'middle', suffix, add_suppl, second_suffix)
top = get_conf(font_config, 'top', suffix, add_suppl, second_suffix)
# using lower version for bottom of quilisma when second is on a line
bottom_lower = get_conf(font_config, 'bottom', suffix, add_suppl, 'lower')
return {
"1": {
# ignoring the suffix for coherence
"short": virga_short,
"long": virga_long,
"open": virga_open
},
"2": {
"short": virga_short,
"long": virga_long
},
"3": {
"short": middle - 3*base,
"long": bottom_lower - 3*base
},
"4": {
"short": top - 4*base,
"long": top - 4*base
},
"5": {
"short": top - 4*base,
"long": top - 4*base
}
}
return {
"ignore j": True,
"Virga": {
"Nothing": {
"short": virga_short,
"open": virga_open,
"long": virga_long
}
},
"Flexus": {
"Nothing": get_basic(''),
"DeminutusFirst": get_basic('deminutus-first'),
"Deminutus": {
"1": {
"short": font_config['middle'] - 2*base,
"long": font_config['top'] - 3*base,
"open": font_config['bottom-deminutus'] - base
},
"2": {
"short": font_config['bottom'] -2*base,
"long": font_config['bottom'] - 2*base,
},
"3": {
"long": font_config['top'] - 3*base,
"short": font_config['top'] - 3*base
},
"4": {
"long": font_config['top'] - 4*base,
"short": font_config['top-deminutus'] - 4*base,
},
"5": {
"long": font_config['top'] - 4*base,
"short": font_config['top-deminutus'] - 4*base,
}
}
},
"PesQuilismaQuadratum": {
"Nothing": get_basic('quilisma', False, 'upper')
},
"PesQuassus": {
"Nothing": get_basic('oriscus', False)
},
"Porrectus": get_default_porrectus(font_config, False, virga_long)
}
def get_stem_schema_solesmes(font_config):
""" This stem schema has been provided directly by a contact
at Abbey of Solesmes.
"""
base = font_config['base height']
def get_bottom(suffix, add_suppl=True, second_suffix=''):
""" Shortcut for get_config with common options
"""
return get_conf(font_config, 'bottom', suffix, add_suppl, second_suffix)
def get_basic(suffix, add_suppl=True, second_suffix=''):
""" Common function for flexus, pes quadratum, pes quassus, etc.
"""
bottom = get_bottom(suffix, add_suppl, second_suffix)
# for ambitus one, it must have the same height as the virga for coherece, so ignoing suffix
bottom_one = get_bottom('', True, second_suffix)
return {
"1": {
"short": bottom_one - base,
"long": bottom_one - 2*base,
"open": bottom_one - base
},
"2": {
"short": bottom - 2*base,
"long": bottom - 2*base
},
"3": {
"short": bottom - 3*base,
"long": bottom - 3*base
},
"4": {
"short": bottom - 4*base,
"long": bottom - 4*base
},
"5": {
"short": bottom - 5*base,
"long": bottom - 5*base
}
}
bottom_virga = get_bottom('')
return {
"ignore j": True,
"Virga": {
"Nothing": {
"short": bottom_virga - base,
"open": bottom_virga - base,
"long": bottom_virga - 2*base
}
},
"Flexus": {
"Nothing": get_basic(''),
"Deminutus": get_basic(''),
"DeminutusFirst": get_basic('deminutus-first')
},
"PesQuilismaQuadratum": {
"Nothing": get_basic('quilisma', False, 'lower')
},
"PesQuassus": {
"Nothing": get_basic('oriscus', False)
},
"Porrectus": get_default_porrectus(font_config, True, bottom_virga - 2*base)
}
def get_stem_schema(schemaname, font_config):
""" Function called by squarize.py, returns the stem schema.
"""
if schemaname == 'default':
return get_stem_schema_default(font_config)
if schemaname == 'solesmes':
return get_stem_schema_solesmes(font_config)
print(f'impossible to find schema {schemaname}, quitting')