-- Copyright (C) 2006-2021 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 . local P = lpeg.P local R = lpeg.R local C = lpeg.C local function sort_keys(table_to_sort, compare) local sorted = {}, key for key in pairs(table_to_sort) do table.insert(sorted, key) end table.sort(sorted, compare) return sorted end local function sort_unique_keys(tables, compare) local set = {}, ignored, table_to_scan, key for ignored, table_to_scan in pairs(tables) do if table_to_scan then for key in pairs(table_to_scan) do set[key] = true end end end return sort_keys(set) end local EXCLUDE = { ['.notdef'] = true, ['.null'] = true, notdef = true, nonmarkingreturn = true, AscendensOriscusLineBLTR = true, AscendensOriscusLineTR = true, BracketLeftZero = true, BracketLeftSix = true, BracketLeftSeven = true, BracketLeftEight = true, BracketLeftNine = true, BracketLeftTen = true, BracketLeftEleven = true, BracketLeftTwelve = true, BracketLeftThirteen = true, BracketLeftFourteen = true, BracketLeftShortZero = true, BracketLeftShortSix = true, BracketLeftShortSeven = true, BracketLeftShortEight = true, BracketLeftShortNine = true, BracketLeftShortTen = true, BracketLeftShortEleven = true, BracketLeftShortTwelve = true, BracketLeftShortThirteen = true, BracketLeftShortFourteen = true, BracketLeftLongZero = true, BracketLeftLongSix = true, BracketLeftLongSeven = true, BracketLeftLongEight = true, BracketLeftLongNine = true, BracketLeftLongTen = true, BracketLeftLongEleven = true, BracketLeftLongTwelve = true, BracketLeftLongThirteen = true, BracketLeftLongFourteen = true, BracketRightZero = true, BracketRightSix = true, BracketRightSeven = true, BracketRightEight = true, BracketRightNine = true, BracketRightTen = true, BracketRightEleven = true, BracketRightTwelve = true, BracketRightThirteen = true, BracketRightFourteen = true, BracketRightShortZero = true, BracketRightShortSix = true, BracketRightShortSeven = true, BracketRightShortEight = true, BracketRightShortNine = true, BracketRightShortTen = true, BracketRightShortEleven = true, BracketRightShortTwelve = true, BracketRightShortThirteen = true, BracketRightShortFourteen = true, BracketRightLongZero = true, BracketRightLongSix = true, BracketRightLongSeven = true, BracketRightLongEight = true, BracketRightLongNine = true, BracketRightLongTen = true, BracketRightLongEleven = true, BracketRightLongTwelve = true, BracketRightLongThirteen = true, BracketRightLongFourteen = true, PunctumAuctusLineBL = true, PunctumLineBLBR = true, PunctumLineBR = true, PunctumLineTR = true, PunctumSmall = true, FlexusLineBL = true, FlexusAmOneLineBL = true, DescendensOriscusLineTR = true, DescendensOriscusLineBLTR = true, QuilismaLineTR = true, VirgaLineBR = true, SalicusOriscus = true, VirgaBaseLineBL = true, ['VirgulaTwo'] = true, ['VirgulaThree'] = true, ['VirgulaFive'] = true, ['VirgulaSix'] = true, ['VirgulaParenTwo'] = true, ['VirgulaParenThree'] = true, ['VirgulaParenFive'] = true, ['VirgulaParenSix'] = true, ['DivisioMinimisTwo'] = true, ['DivisioMinimisThree'] = true, ['DivisioMinimisFive'] = true, ['DivisioMinimisSix'] = true, ['DivisioMinimaTwo'] = true, ['DivisioMinimaThree'] = true, ['DivisioMinimaFive'] = true, ['DivisioMinimaSix'] = true, ['DivisioMinimaParenTwo'] = true, ['DivisioMinimaParenThree'] = true, ['DivisioMinimaParenFive'] = true, ['DivisioMinimaParenSix'] = true, ['DivisioMinorTwo'] = true, ['DivisioMinorThree'] = true, ['DivisioMinorFive'] = true, ['DivisioMaiorTwo'] = true, ['DivisioMaiorThree'] = true, ['DivisioMaiorFive'] = true, ['DivisioMaiorDottedTwo'] = true, ['DivisioMaiorDottedThree'] = true, ['DivisioMaiorDottedFive'] = true, ['DivisioMaiorDottedBackingTwo'] = true, ['DivisioMaiorDottedBackingThree'] = true, ['DivisioMaiorDottedBackingFive'] = true, } -- &&& in the following two tables is a placeholder for the cavum shape 'r' local GABC = { Accentus = [[\excluded{g}r1]], AccentusReversus = [[\excluded{g}r2]], Ancus = [[g&&&ec]], AncusLongqueue = [[h&&&fd]], AscendensOriscus = [[g&&&o1]], AscendensOriscusLineBL = [[\excluded{e}@g&&&o1]], AscendensOriscusLineTL = [[\excluded{i}@g&&&o1]], AscendensOriscusScapus = [[g&&&O1]], AscendensOriscusScapusLongqueue = [[h&&&O1]], AscendensOriscusScapusOpenqueue = [[a&&&O1]], AscendensPunctumInclinatum = [[G1&&&]], AuctumMora = [[\excluded{g}.]], BarBrace = [[\excluded{,}\_]], BracketLeft = [=[[[\excluded{ce]]}]=], BracketLeftShort = [=[[[\excluded{fh]]}]=], BracketLeftLong = [=[[[\excluded{gi]]}]=], BracketRight = [=[\excluded{[[ce}]]]=], BracketRightShort = [=[\excluded{[[fh}]]]=], BracketRightLong = [=[\excluded{[[gi}]]]=], CClef = [[c3]], CClefChange = [[c3]], Circulus = [[\excluded{g}r3]], CurlyBrace = '[ocb:1;6mm]', CustosDownLong = [[j+]], CustosDownMedium = [[m+]], CustosDownShort = [[k+]], CustosUpLong = [[f+]], CustosUpMedium = [[a+]], CustosUpShort = [[g+]], DescendensOriscus = [[g&&&o0]], DescendensOriscusLineBL = [[\excluded{e}@g&&&o0]], DescendensOriscusLineTL = [[\excluded{i}@g&&&o0]], DescendensOriscusScapus = [[g&&&O0]], DescendensOriscusScapusLongqueue = [[h&&&O0]], DescendensOriscusScapusOpenqueue = [[a&&&O0]], DescendensPunctumInclinatum = [[G0&&&]], DivisioDominican = [[,3]], DivisioDominicanAlt = [[,4]], DivisioMaiorFour = [[:]], DivisioMaiorDottedFour = [[:?]], DivisioMaiorDottedBackingFour = [[\excluded{:?}]], DivisioMinimaFour = [[,]], DivisioMinimaParenFour = [[,?]], DivisioMinimisFour = [[\textasciicircum{}]], DivisioMinorFour = [[;]], FClefChange = [[f3]], FClef = [[f3]], Flat = [[gx]], FlatHole = [[\excluded{gx}]], FlatParen = [[gx?]], FlatParenHole = [[\excluded{gx?}]], Flexus = [[g&&&e]], FlexusLongqueue = [[h&&&f]], FlexusNobar = [[@h&&&f]], FlexusOriscus = [[g&&&oe]], FlexusOriscusInusitatus = [[g&&&o1e]], FlexusOriscusScapus = [[g&&&Oe]], FlexusOriscusScapusInusitatus = [[g&&&O1e]], FlexusOriscusScapusInusitatusLongqueue = [[h&&&O1f]], FlexusOriscusScapusLongqueue = [[h&&&Of]], LeadingOriscus = [[g&&&o\excluded{igig}]], LeadingPunctum = [[g&&&\excluded{igig}]], LeadingQuilisma = [[g&&&w\excluded{igig}]], Linea = [[g&&&=]], LineaPunctum = [[g&&&R]], Natural = [[gy]], NaturalHole = [[\excluded{gy}]], NaturalParen = [[gy?]], NaturalParenHole = [[\excluded{gy?}]], OblatusAscendensOriscus = [[g&&&o1]], OblatusDescendensOriscus = [[g&&&o0]], OblatusFlexusOriscus = [[g&&&oe]], OblatusFlexusOriscusInusitatus = [[g&&&o1e]], OblatusPesQuassus = [[g&&&oi]], OblatusPesQuassusLongqueue = [[h&&&oj]], OblatusPesQuassusInusitatus = [[g&&&o0i]], OblatusPesQuassusInusitatusLongqueue = [[h&&&o0j]], Oriscus = [[g&&&o]], -- for Deminutus Pes = [[g&&&i]], PesAscendensOriscus = [[g&&&iO\excluded{/j}]], PesDescendensOriscus = [[g&&&iO\excluded{/h}]], PesQuadratum = [[g&&&qi]], PesQuadratumLongqueue = [[h&&&qj]], PesQuassus = [[g&&&oi]], PesQuassusInusitatus = [[g&&&o0i]], PesQuassusInusitatusLongqueue = [[h&&&o0j]], PesQuassusLongqueue = [[h&&&oj]], PorrectusFlexus = [[g&&&ege]], PorrectusFlexusNobar = [[\excluded{e}g&&&ege]], Porrectus = [[g&&&eg]], PorrectusLongqueue = [[h&&&fh]], PorrectusNobar = [[@g&&&eg]], Punctum = [[g&&&]], PunctumInclinatum = [[G&&&]], -- for deminutus PunctumInclinatumAuctus = [[G&&&>]], PunctumLineBL = [[\excluded{e}@g&&&]], PunctumLineTL = [[\excluded{i}@g&&&]], Quilisma = [[g&&&w]], QuilismaPes = [[g&&&wi]], QuilismaPesQuadratum = [[g&&&Wi]], QuilismaPesQuadratumLongqueue = [[h&&&Wj]], RoundBraceDown = '[ub:1;6mm]', RoundBrace = '[ob:1;6mm]', SalicusFlexus = [[g&&&iOki]], Salicus = [[g&&&iOk]], SalicusLongqueue = [[h&&&jOl]], Scandicus = [[g&&&ik]], Semicirculus = [[\excluded{g}r4]], SemicirculusReversus = [[\excluded{g}r5]], Sharp = [[g\#{}]], SharpHole = [[\excluded{g\#{}}]], SharpParen = [[g\#{}?]], SharpParenHole = [[\excluded{g\#{}?}]], StansPunctumInclinatum = [[G2&&&]], StrophaAucta = [[g&&&s>]], StrophaAuctaLongtail = [[h&&&s>]], Stropha = [[g&&&s]], Torculus = [[g&&&ig]], TorculusLiquescens = [[g&&&ige]], TorculusLiquescensQuilisma = [[g&&&wige]], TorculusQuilisma = [[g&&&wig]], TorculusResupinus = [[g&&&igi]], TorculusResupinusQuilisma = [[g&&&wigi]], VEpisema = [[\excluded{g}^^^^0027]], Virga = [[g&&&v]], VirgaLongqueue = [[h&&&v]], VirgaOpenqueue = [[a&&&v]], VirgaReversa = [[g&&&V]], VirgaReversaLongqueue = [[h&&&V]], VirgaReversaOpenqueue = [[a&&&V]], VirgulaFour = [[^^^^0060]], VirgulaParenFour = [[^^^^0060?]], } local GABC_AMBITUS_ONE = { PorrectusLongqueue = [[h&&&gh]], PorrectusFlexusLongqueue = [[h&&&ghg]], FlexusOpenqueue = [[b&&&a]], FlexusOriscusScapusOpenqueue = [[b&&&Oa]], PesQuadratumOpenqueue = [[a&&&qb]], PesQuassusOpenqueue = [[a&&&ob]], QuilismaPesQuadratumOpenqueue = [[a&&&Wb]], OblatusPesQuassusInusitatusOpenqueue = [[a&&&o0b]], OblatusPesQuassusOpenqueue = [[b&&&oc]], } -- if the item is a table, the values will replace fuse_head and gabc local GABC_FUSE = { Upper = { Punctum = [[\excluded{e}@]], AscendensOriscus = [[\excluded{e}@]], DescendensOriscus = [[\excluded{e}@]], OblatusAscendensOriscus = [[\excluded{f}@]], OblatusFlexusOriscusInusitatus = [[\excluded{f}@]], OblatusPesQuassus = [[\excluded{f}@]], OblatusPesQuassusLongqueue = [[\excluded{g}@]], OblatusPesQuassusOpenqueue = [[\excluded{a}@]], Pes = [[\excluded{e}@]], PesQuadratum = [[\excluded{e}@]], PesQuadratumLongqueue = [[\excluded{f}@]], PesQuadratumOpenqueue = { [[\excluded{a}@]], [[bqc]] }, PesQuassus = [[\excluded{e}@]], PesQuassusInusitatus = [[\excluded{e}@]], PesQuassusInusitatusLongqueue = [[\excluded{f}@]], PesQuassusLongqueue = [[\excluded{f}@]], PesQuassusOpenqueue = { [[\excluded{a}@]], [[cod]] }, Flexus = [[\excluded{e}@]], FlexusOriscus = [[\excluded{e}@]], FlexusOriscusInusitatus = [[\excluded{e}@]], }, Lower = { Punctum = [[\excluded{i}@]], AscendensOriscus = [[\excluded{i}@]], DescendensOriscus = [[\excluded{i}@]], OblatusDescendensOriscus = [[\excluded{h}@]], OblatusFlexusOriscus = [[\excluded{h}@]], OblatusPesQuassusInusitatus = [[\excluded{h}@]], OblatusPesQuassusInusitatusLongqueue = [[\excluded{i}@]], OblatusPesQuassusInusitatusOpenqueue = [[\excluded{b}@]], Pes = [[\excluded{i}@]], PesQuadratum = [[\excluded{i}@]], PesQuadratumLongqueue = [[\excluded{j}@]], PesQuadratumOpenqueue = [[\excluded{b}@]], PesQuassus = [[\excluded{i}@]], PesQuassusInusitatus = [[\excluded{i}@]], PesQuassusInusitatusLongqueue = [[\excluded{j}@]], PesQuassusLongqueue = [[\excluded{j}@]], PesQuassusOpenqueue = [[\excluded{b}@]], Flexus = [[\excluded{i}@]], FlexusOriscus = [[\excluded{i}@]], FlexusOriscusInusitatus = [[\excluded{i}@]], }, Up = { Punctum = [[\excluded{@ij}]], AscendensOriscus = [[\excluded{@ij}]], AscendensOriscusScapus = [[\excluded{@ij}]], AscendensOriscusScapusLongqueue = [[\excluded{@jk}]], DescendensOriscus = [[\excluded{@ij}]], DescendensOriscusScapus = [[\excluded{@ij}]], DescendensOriscusScapusLongqueue = [[\excluded{@jk}]], OblatusAscendensOriscus = [[\excluded{@i}]], OblatusDescendensOriscus = [[\excluded{@i}]], Quilisma = [[\excluded{@ij}]], Flexus = [[\excluded{@gi}]], FlexusNobar = [[\excluded{@hj}]], }, Down = { Punctum = [[\excluded{@eg}]], AscendensOriscus = [[\excluded{@eg}]], AscendensOriscusScapus = [[\excluded{@eg}]], AscendensOriscusScapusLongqueue = [[\excluded{@eg}]], DescendensOriscus = [[\excluded{@eg}]], DescendensOriscusScapus = [[\excluded{@eg}]], DescendensOriscusScapusLongqueue = [[\excluded{@eg}]], OblatusAscendensOriscus = [[\excluded{@e}]], OblatusDescendensOriscus = [[\excluded{@e}]], VirgaReversa = [[\excluded{@eg}]], VirgaReversaLongqueue = [[\excluded{@fg}]], }, } local DEBILIS = { InitioDebilis = [[-]], [''] = [[]], } local LIQUESCENCE = { Ascendens = [[<]], Descendens = [[>]], Deminutus = [[\~{}]], Nothing = [[]], [''] = [[]], } GregorioRef = {} function GregorioRef.emit_score_glyphs(cs_normal, cs_hollow) local common_glyphs = {} local normal_variants = {} local normal_names = {} local hollow_variants = {} local hollow_names = {} local function index_font(csname, variants, names, common) local glyphs = font.fonts[font.id(csname)].resources.unicodes -- force-load the code points of the font -- local ignored = glyphs['___magic___'] local glyph, cp for glyph, cp in pairs(glyphs) do names[glyph] = true if cp >= 0xe000 and not EXCLUDE[glyph] and not glyph:match('^HEpisema') then local name, variant = glyph:match('^([^.]*)(%.%a*)$') if name then local glyph_variants = variants[name] if glyph_variants == nil then glyph_variants = {} variants[name] = glyph_variants end glyph_variants[variant] = cp elseif common then common[glyph] = cp end end end end index_font(cs_normal, normal_variants, normal_names, common_glyphs) index_font(cs_hollow, hollow_variants, hollow_names, common_glyphs) local function maybe_emit_glyph(csname, variants, name, variant) local cp = variants[name] if cp then cp = cp[variant] if cp then tex.sprint(string.format([[&{\%s\char%d}]], csname, cp)) end end if not cp then tex.sprint(string.format([[&{\tiny\itshape N/A}]], csname, cp)) end end local function emit_score_glyph(fusion, shape, ambitus, debilis, liquescence) local name = fusion..shape..ambitus..debilis..liquescence local char = common_glyphs[name] local gabc = GABC[shape] or GABC_AMBITUS_ONE[shape] if gabc then local fuse_head = '' local fuse_tail = '' if fusion ~= '' then fuse_head = GABC_FUSE[fusion][shape] if fuse_head == nil then tex.error('No head fusion for '..name) end if type(fuse_head) == 'table' then fuse_head, gabc = fuse_head[1], fuse_head[2] end end local liq = liquescence if liq == 'Up' or liq == 'Down' then fuse_tail = GABC_FUSE[liq][shape] if fuse_tail == nil then tex.error('No tail fusion for '..name) end liq = '' end gabc = '('..fuse_head..DEBILIS[debilis]..gabc..LIQUESCENCE[liq]..fuse_tail..')' else texio.write_nl('GregorioRef Warning: missing GABC for '..name) end local sorted_normal = sort_unique_keys{normal_variants[name]} local sorted_hollow = sort_unique_keys{hollow_variants[name]} local n = math.max(1, #sorted_normal, #sorted_hollow) local emitted = false, i, variant for i = 1,n do if emitted then tex.sprint([[\nopagebreak&&&]]) else tex.sprint(string.format( [[{%s\scriptsize %s{\bfseries %s}{\itshape %s}%s%s}&{\ttfamily\small %s}&{\%s\char%d}&]], gabc and [[]] or [[\color{red}]], fusion, shape, ambitus, debilis, liquescence, gabc and gabc:gsub('&&&', '') or '', cs_normal, char )) end variant = sorted_normal[i] if variant then tex.sprint(string.format([[{\scriptsize %s}]], variant)) maybe_emit_glyph(cs_normal, normal_variants, name, variant) else tex.print([[&]]) end if emitted or not hollow_names[name] then tex.sprint([[&&&]]) else tex.sprint(string.format( [[&{\ttfamily\small %s}&{\%s\char%d}&]], gabc and gabc:gsub('&&&', 'r') or '', cs_hollow, char )) end variant = sorted_hollow[i] if variant then tex.sprint(string.format([[{\scriptsize %s}]], variant)) maybe_emit_glyph(cs_hollow, hollow_variants, name, variant) else tex.print([[&]]) end tex.print([[\\]]) emitted = true end end local glyph_names = {} local ambitus = P'One' + P'Two' + P'Three' + P'Four' + P'Five' local majuscule = R'AZ' local minuscule = R'az' local fusion = P'Upper' + P'Lower' local debilis = P'InitioDebilis' local post_word_liquescentia = P'Nothing' + P'Deminutus' + P'Ascendens' + P'Descendens' local liquescentia = post_word_liquescentia + P'Up' + P'Down' local word = ((majuscule * minuscule^0) - fusion - ambitus - debilis - post_word_liquescentia) + ((P'Ascendens' + P'Descendens') * P'Oriscus') local liquescence = debilis^-1 * liquescentia^-1 local pattern = C(fusion^-1) * C(word^1) * C(ambitus^0) * C(debilis^-1) * C(liquescentia^-1) * -1 local only_twos = P'Two'^1 * -1 local ambitus_one = P'One' * P'Two'^0 * -1 for name in pairs(common_glyphs) do local a, b, c, d, e = pattern:match(name) if b then table.insert(glyph_names, { a, b, c, d, e }) else -- if parse fails, just use the name table.insert(glyph_names, { '', name, '', '', '' }) end end local function compare(x, y) local nx = x[1]..x[2] local ny = y[1]..y[2] if nx < ny then return true elseif nx == ny then if x[4] < y[4] then return true elseif x[4] == y[4] then if x[5] < y[5] then return true elseif x[5] == y[5] and x[3] < y[3] then return true end end end return false end table.sort(glyph_names, compare) local first = true local i, name for i, name in ipairs(glyph_names) do local shape = name[2] local ambitus = name[3] if shape:match('^Virgula') or shape:match('^Divisio') then shape = shape..ambitus ambitus = '' end if not EXCLUDE[shape] then if (ambitus == '' and name[5] == '') or ambitus == '' or only_twos:match(ambitus) or (GABC_AMBITUS_ONE[shape] and ambitus_one:match(ambitus)) then if first then first = false else tex.print([[\hline]]) end emit_score_glyph(name[1], shape, ambitus, name[4], name[5]) end end end end function GregorioRef.emit_extra_glyphs(csname) local glyphs = font.fonts[font.id(csname)].resources.unicodes local first = true local odd = true for i, name in ipairs(sort_keys(glyphs)) do local cp = glyphs[name] if cp >= 0xe000 and not EXCLUDE[name] then if first then first = false elseif odd then tex.print([[\hline]]) end tex.sprint(string.format([[{\scriptsize %s}&{\%s\char%d}]], name, csname, cp)) if odd then tex.sprint([[&]]) else tex.print([[\\]]) end odd = not odd end end if not odd then tex.print([[&\\]]) end end function GregorioRef.emit_dimension(value) value = string.gsub(value, '(-?%d+%.%d+)%s*(%a+)', [[\unit[%1]{%2}]]) value = string.gsub(value, '(-?%d+%.)%s*(%a+)', [[\unit[%1]{%2}]]) value = string.gsub(value, '(-?%.?%d+)%s*(%a+)', [[\unit[%1]{%2}]]) tex.sprint(value) end