diff --git a/CHANGELOG.md b/CHANGELOG.md index 363a8715..faa99b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,16 @@ As of v3.0.0 this project adheres to [Semantic Versioning](http://semver.org/). - Decouple visibility control for NABC and above-lines text from notes. Solves [#1547](https://github.com/gregorio-project/gregorio/issues/1547). - Added proper vertical spacing and alignment of score elements in NABC+lyrics-only contexts. Solves [#1694](https://github.com/gregorio-project/gregorio/issues/1694). - Added horizontal spacing preservation for NABC neumes, preventing overlap. Solves [#1699](https://github.com/gregorio-project/gregorio/issues/1699). +- Added `\gresetnabcinterglyphpadding` command to add compensating horizontal space between glyphs when NABC neumes overflow beyond the glyph width. Prevents NABC overlap at glyph boundaries within a syllable. Supports per-voice configuration with optional voice parameter. - Added overtie/undertie special symbols (`ut` for `\greundertie`, `ot` for `\greovertie`, and `dt` for `\gredoubletie`), and a configurable lyric tying shorthand (`~` for `\GreLyricTie`). - Added support for the C23 standard (the default in GCC 15). The included build scripts continue to default to GNU89 C. +- Added `nabcintersyllablemingap` distance (configurable via `\grechangedim`), which sets a minimum separation between NABC glyphs of consecutive syllables. Defaults to 0.06 cm (25% of `intersyllablespacenotes`). See [#1715](https://github.com/gregorio-project/gregorio/issues/1715). ### Fixed - Fixed a bug that could cause a punctum mora that is supposed to be below the line (`.0`) to appear above the line. This bug was platform-dependent and was observed on a Windows system. See [#1642](https://github.com/gregorio-project/gregorio/issues/1642). - Error messages from executable have been cleaned up to be more uniform. See [#1644](https://github.com/gregorio-project/gregorio/issues/1644). - NABC neumes are now rendered for syllables with empty GABC/NABC snippets when NABC content is present (e.g. `(|vi|ta)`, `(|vi)`, `(||ta)`, `(g||ta)`). See [#1700](https://github.com/gregorio-project/gregorio/issues/1700). +- Fixed excessive inter-syllable spacing caused by the NABC extrakern mechanism being applied even when the natural spacing between syllables is already sufficient to prevent NABC overlap. See [#1715](https://github.com/gregorio-project/gregorio/issues/1715). ## [Unreleased][CTAN] ### Fixed diff --git a/doc/Command_Index_User.tex b/doc/Command_Index_User.tex index 259e226e..6764b45e 100644 --- a/doc/Command_Index_User.tex +++ b/doc/Command_Index_User.tex @@ -1840,6 +1840,10 @@ \subsection{Distances}\label{distances} Larger space between elements in ancient notation. \end{gdimension} +\begin{gdimension}{nabcintersyllablemingap} +Minimum gap between NABC glyphs of consecutive syllables. When NABC neumes extend beyond the notes box of a syllable, Gregorio\TeX\ computes an overflow that pushes the next syllable to the right to prevent NABC overlap. This distance sets a minimum separation that is enforced even when the NABC would otherwise just barely fit. The default value (0.06\,cm) is 25\% of \texttt{intersyllablespacenotes} and provides a comfortable visual margin. Set to \texttt{0pt} to disable the minimum gap and rely solely on physical overflow. +\end{gdimension} + \begin{gdimension}{clivisalignmentmin} When \verb=\gre@clivisalignment= is 2, this distance is the maximum length of the consonants after vowels for which the clivis will be aligned on its center. \end{gdimension} diff --git a/doc/Command_Index_internal.tex b/doc/Command_Index_internal.tex index e92569b0..aca69963 100644 --- a/doc/Command_Index_internal.tex +++ b/doc/Command_Index_internal.tex @@ -790,8 +790,8 @@ \section{Gregorio\TeX{} Controls} \#2 & content & The note material to render.\\ \end{argtable} -\macroname{\textbackslash gre@compute@nabc@extrakern}{}{gregoriotex-syllable.tex} -Computes extra horizontal space needed when NABC neumes extend past the total width of the syllable notes. If \verb=\gre@dimen@nabc@maxwidth= exceeds the width of \verb=\gre@box@syllablenotes=, the difference is stored in \verb=\gre@dimen@nabc@extrakern= and a kern of that amount is appended to the syllable notes box. +\macroname{\textbackslash gre@compute@nabc@syllable@overflow}{}{gregoriotex-syllable.tex} +Computes the inter-syllable right-overflow needed to prevent NABC neumes of adjacent syllables from colliding. Uses \verb=\gre@dimen@nabc@maxwidth= (widest NABC across all voices) plus \verb=\gre@space@dimen@nabcintersyllablemingap= minus the original notes width (before end-difference reduction); the result is clamped to zero. Stored in \verb=\gre@dimen@nabc@syllable@overflow= and carried forward to the next syllable as \verb=\gre@dimen@nabc@syllable@prevoverflow=. \macroname{\textbackslash gre@check@nabc@needsspacing}{}{gregoriotex-syllable.tex} Checks if any NABC voice is visible and sets \verb=\ifgre@nabc@needsspacing= accordingly. When true, horizontal spacing must be preserved even when notes are invisible, to prevent NABC neume overlap. @@ -804,7 +804,7 @@ \section{Gregorio\TeX{} Controls} \end{argtable} \macroname{\textbackslash gre@notes@rendernabc@tight}{\#1}{gregoriotex-syllable.tex} -Renders NABC neumes without preserving any note glyph or inter-element spacing. Uses \verb=\gre@nabconlyglyph= instead of \verb=\GreGlyph= and suppresses all end-of-element and end-of-glyph callbacks, as well as bar output. The total horizontal width comes entirely from \verb=\gre@compute@nabc@extrakern=. This produces tighter spacing than the hphantom path. +Renders NABC neumes without preserving any note glyph or inter-element spacing. Uses \verb=\gre@nabconlyglyph= instead of \verb=\GreGlyph= and suppresses all end-of-element and end-of-glyph callbacks, as well as bar output. The total horizontal width is provided by the inter-syllable overflow mechanism (\verb=\gre@dimen@nabc@syllable@prevoverflow=) rather than by an in-box kern. This produces tighter spacing than the hphantom path. \begin{argtable} \#1 & content & The notes material to process for NABC rendering.\\ @@ -1472,7 +1472,7 @@ \section{Gregorio\TeX{} Controls} \end{argtable} \macroname{\textbackslash gre@nabc@prepare}{\#1\#2}{gregoriotex-main.tex} -Phase 1 of NABC two-phase processing: pre-evaluates NABC content, tracks the maximum NABC width in \verb=\gre@dimen@nabc@maxwidth= for right-overflow detection by \verb=\gre@compute@nabc@extrakern=, emits note-level kern for left overflow (in \texttt{neume} alignment mode) unless \verb=\ifgre@nabc@suppress@leftoverflow= is true, and saves the rendered content in a box register. +Phase 1 of NABC two-phase processing: pre-evaluates NABC content, tracks the maximum NABC width in \verb=\gre@dimen@nabc@maxwidth= for right-overflow detection by \verb=\gre@compute@nabc@syllable@overflow=, emits note-level kern for left overflow (in \texttt{neume} alignment mode) unless \verb=\ifgre@nabc@suppress@leftoverflow= is true, and saves the rendered content in a box register. \begin{argtable} \#1 & content & The NABC content (from \textbackslash GreNABCChar)\\ @@ -2357,10 +2357,13 @@ \subsection{Distances} Saved (pre-zeroed) value of the total staff height. This dimension always holds the true computed value of \verb=\gre@dimen@staffheight= regardless of whether staff dimensions have been zeroed. Computed from \verb=\gre@dimen@stafflineheight@saved= and \verb=\gre@dimen@interstafflinespace@saved=. \macroname{\textbackslash gre@dimen@nabc@maxwidth}{}{gregoriotex-syllable.tex} -Tracks the maximum NABC width for the current syllable across all voices. Used to detect when NABC extends past the total glyph width and extra horizontal space is needed at the end of the syllable to prevent overlap with the next NABC. Reset to 0pt at the start of each syllable. +Tracks the maximum NABC width for the current syllable across all voices. Used by \verb=\gre@compute@nabc@syllable@overflow= to determine if NABC extends past the notes and extra space is needed. Reset to 0pt at the start of each syllable. -\macroname{\textbackslash gre@dimen@nabc@extrakern}{}{gregoriotex-syllable.tex} -Stores the extra kern needed at the end of the syllable to accommodate NABC overflow. Computed by \verb=\gre@compute@nabc@extrakern= during the pre-measurement pass in \verb=\gre@syllablenotes= and applied after the real notes typesetting in \verb=\GreSyllable=. Reset to 0pt at the start of each syllable. +\macroname{\textbackslash gre@dimen@nabc@syllable@overflow}{}{gregoriotex-syllable.tex} +Stores the inter-syllable right-overflow computed by \verb=\gre@compute@nabc@syllable@overflow= for the current syllable. Reduced by \verb=\gre@dimen@syllablefinalskip= and \verb=\gre@dimen@nextbegindifference= before being saved as \verb=\gre@dimen@nabc@syllable@prevoverflow=. Reset to 0pt at the start of each syllable. + +\macroname{\textbackslash gre@dimen@nabc@syllable@prevoverflow}{}{gregoriotex-syllable.tex} +Carries the right-overflow of the previous syllable into the current one. A kern of this amount is prepended before the notes of the current syllable (and subtracted from \verb=\gre@dimen@notesaligncenter=) so that the NABC of the previous syllable does not collide with that of the current syllable. Discarded if the current syllable has no NABC. Reset at the start of each syllable. \macroname{\textbackslash gre@dimen@glyphraisevalue}{}{gregoriotex-spaces.tex} The value that a particular glyph must be raised to be set in the correct position. diff --git a/tex/gregoriotex-gsp-default.tex b/tex/gregoriotex-gsp-default.tex index c1868ab8..e329ad74 100644 --- a/tex/gregoriotex-gsp-default.tex +++ b/tex/gregoriotex-gsp-default.tex @@ -118,6 +118,10 @@ \gre@createdim{skip}{nabcinterelementspace}{0.06927 cm plus 0.00182 cm minus 0.00363 cm}{scalable}% % larger space between elements in ancient notation \gre@createdim{skip}{nabclargerspace}{0.10938 cm plus 0.01822 cm minus 0.00911 cm}{scalable}% +% minimum gap between NABC glyphs of consecutive syllables; when the natural +% inter-syllable separation would produce a smaller gap, a compensating kern +% is added. Default: 25% of intersyllablespacenotes. +\gre@createdim{dimen}{nabcintersyllablemingap}{0.06 cm}{scalable}% % space between elements which has the size of a note \gre@createdim{skip}{glyphspace}{0.21877 cm plus 0.01822 cm minus 0.01822 cm}{scalable}% % space before in-line custos diff --git a/tex/gregoriotex-main.tex b/tex/gregoriotex-main.tex index fc812825..b200658c 100644 --- a/tex/gregoriotex-main.tex +++ b/tex/gregoriotex-main.tex @@ -703,9 +703,9 @@ % nodes do not carry a stray part=7 when preparing voice 2. \def\gre@nabc@prepare#1#2{% \setbox\gre@box@nabctemp=\hbox{#1}% - % Track maximum NABC width for right-overflow detection (extra kern at - % syllable end). Without this, \gre@compute@nabc@extrakern cannot detect - % when NABC content extends past the syllable notes width. + % Track maximum NABC width for inter-syllable right-overflow detection. + % This allows \gre@compute@nabc@syllable@overflow to detect when NABC + % content extends past the effective syllable width. \ifdim\wd\gre@box@nabctemp>\gre@dimen@nabc@maxwidth\relax% \global\gre@dimen@nabc@maxwidth=\wd\gre@box@nabctemp\relax% \fi% diff --git a/tex/gregoriotex-spaces.tex b/tex/gregoriotex-spaces.tex index 74838c40..ee89a0c7 100644 --- a/tex/gregoriotex-spaces.tex +++ b/tex/gregoriotex-spaces.tex @@ -1771,6 +1771,9 @@ \ifgre@scale@nabclargerspace% \gre@changeonedimenfactor{nabclargerspace}{#1}{#2}% \fi% + \ifgre@scale@nabcintersyllablemingap% + \gre@changeonedimenfactor{nabcintersyllablemingap}{#1}{#2}% + \fi% \ifgre@scale@glyphspace% \gre@changeonedimenfactor{glyphspace}{#1}{#2}% \fi% diff --git a/tex/gregoriotex-syllable.tex b/tex/gregoriotex-syllable.tex index d1b67584..eec7b142 100644 --- a/tex/gregoriotex-syllable.tex +++ b/tex/gregoriotex-syllable.tex @@ -428,32 +428,59 @@ }% % Tracks the maximum NABC width for the current syllable across all voices. -% Used to detect when NABC extends past the total glyph width and extra -% horizontal space is needed at the end of the syllable to prevent overlap -% with the next NABC. +% Used to detect when NABC extends past the effective syllable width. \newdimen\gre@dimen@nabc@maxwidth \gre@dimen@nabc@maxwidth=0pt\relax -% Stores the extra kern needed at the end of the syllable to accommodate -% NABC overflow. Computed during the pre-measurement pass in -% \gre@syllablenotes and applied after the real notes typesetting in -% \GreSyllable. -\newdimen\gre@dimen@nabc@extrakern -\gre@dimen@nabc@extrakern=0pt\relax - -% If NABC extends past the total syllable notes width, compute and save -% the extra kern, then widen \gre@box@syllablenotes accordingly. -% Called after \gre@box@syllablenotes has been filled. The kern is -% applied at syllable level (not per-glyph) to avoid splitting -% multi-glyph neumes like ce/fgf or f!gwh/ih/hf/fe~. -\def\gre@compute@nabc@extrakern{% - \ifdim\gre@dimen@nabc@maxwidth>\wd\gre@box@syllablenotes\relax - \global\gre@dimen@nabc@extrakern=\dimexpr( - \gre@dimen@nabc@maxwidth - \wd\gre@box@syllablenotes)\relax - \setbox\gre@box@syllablenotes=\hbox{% - \unhbox\gre@box@syllablenotes - \kern\gre@dimen@nabc@extrakern - }% +% Tracks the NABC right-overflow of the current syllable: how much the +% widest NABC extends past the effective syllable width (accounting for +% both notes and text). Computed during the GreSyllable body (after +% \gre@calculate@enddifference) and consumed by the next syllable's +% pre-measurement pass. +\newdimen\gre@dimen@nabc@syllable@overflow +\gre@dimen@nabc@syllable@overflow=0pt\relax + +% Saved overflow from the previous syllable. If the current syllable +% also has NABC content, an inter-syllable kern equal to this value is +% prepended to the notes, shifting them (and their NABC) right to +% prevent overlap. +\newdimen\gre@dimen@nabc@syllable@prevoverflow +\gre@dimen@nabc@syllable@prevoverflow=0pt\relax + +% Compute the NABC right-overflow for the current syllable, accounting +% for both notes and text width. The overflow is inflated by the +% nabcintersyllablemingap threshold so that the natural inter-syllable +% reductions must absorb both the raw overshoot AND the minimum gap +% before the kern is eliminated. The overflow is reduced when text +% extends past the notes' +% right edge (\gre@dimen@enddifference < 0), since that extra text +% width provides natural separation from the next syllable's NABC. +% In normal syllables (GreSyllable), the overflow is further reduced by +% the inter-syllable gap (syllablefinalskip + nextbegindifference) at +% the call site; this provides a more accurate estimate of the actual +% overlap risk. +\def\gre@compute@nabc@syllable@overflow{% + % original_noteswd = boxwd - prevoverflow (prevoverflow was prepended) + \gre@dimen@temp@one=\dimexpr( + \wd\gre@box@syllablenotes - \gre@dimen@nabc@syllable@prevoverflow)\relax + % Guard: only compute when the syllable actually has NABC content. + % The condition uses maxwidth > 0pt (not maxwidth > noteswd) because + % the mingap threshold can make the overflow positive even when the + % NABC fits within the notes box. + \ifdim\gre@dimen@nabc@maxwidth>0pt\relax + \global\gre@dimen@nabc@syllable@overflow=\dimexpr( + \gre@dimen@nabc@maxwidth + \gre@space@dimen@nabcintersyllablemingap + - \gre@dimen@temp@one)\relax + % Reduce by text extending past the notes' right edge. + \ifdim\gre@dimen@enddifference<0pt\relax + \global\advance\gre@dimen@nabc@syllable@overflow + by \gre@dimen@enddifference\relax + \fi + \ifdim\gre@dimen@nabc@syllable@overflow<0pt\relax + \global\gre@dimen@nabc@syllable@overflow=0pt\relax + \fi + \else + \global\gre@dimen@nabc@syllable@overflow=0pt\relax \fi }% @@ -485,10 +512,10 @@ }% % Render NABC without preserving any note glyph or inter-element spacing. -% The total horizontal width comes entirely from \gre@compute@nabc@extrakern, -% which expands the syllable box to the NABC width. This produces tighter -% spacing than the hphantom path because inter-glyph and inter-element glue -% is suppressed. +% The total horizontal width comes from the inter-syllable right-overflow +% mechanism, which carries NABC overflow forward to the next syllable. +% This produces tighter spacing than the hphantom path because inter-glyph +% and inter-element glue is suppressed. \def\gre@notes@rendernabc@tight#1{% \begingroup \let\GreGlyph\gre@nabconlyglyph @@ -538,14 +565,15 @@ \global\gre@count@lastglyphiscavum=0\relax % \global\gre@firstglyphtrue% \global\gre@boxingtrue% + % Save previous syllable's overflow for inter-syllable compensation. + \gre@dimen@nabc@syllable@prevoverflow=\gre@dimen@nabc@syllable@overflow\relax% + \global\gre@dimen@nabc@syllable@overflow=0pt\relax% % Reset NABC tracking for this syllable. \global\gre@dimen@nabc@maxwidth=0pt\relax % - \global\gre@dimen@nabc@extrakern=0pt\relax % \let\ifgre@saved@prenotes@lastendswithmora\ifgre@lastendswithmora% \xdef\gre@saved@prenotes@lastoflinecount{\number\gre@lastoflinecount\relax }% \ifgre@shownotes% \setbox\gre@box@syllablenotes=\hbox{#1}% - \gre@compute@nabc@extrakern \else% \ifnum\gre@notes@phantomwrapper>0\relax \setbox\gre@box@syllablenotes=\hbox{\gre@applynotesphantom{\gre@notes@phantomwrapper}{#1}}% @@ -553,12 +581,17 @@ \gre@check@nabc@needsspacing \ifgre@nabc@needsspacing \setbox\gre@box@syllablenotes=\hbox{\gre@notes@rendernabc@tight{#1}}% - \gre@compute@nabc@extrakern \else \setbox\gre@box@syllablenotes=\box\voidb@x% \fi \fi \fi% + % Discard previous overflow if current syllable has no NABC. + \ifdim\gre@dimen@nabc@syllable@prevoverflow>0pt\relax% + \ifdim\gre@dimen@nabc@maxwidth>0pt\relax\else% + \gre@dimen@nabc@syllable@prevoverflow=0pt\relax% + \fi% + \fi% \gre@debugmsg{spacing}{Width of notes: \the\wd\gre@box@syllablenotes}% \global\gre@boxingfalse% \global\gre@firstglyphtrue% @@ -1049,6 +1082,16 @@ \global\gre@dimen@nabcleftoverflow=0pt\relax% \global\gre@dimen@nabckernemitted=0pt\relax% \fi% + % Inter-syllable NABC right-overflow: if the previous syllable's NABC + % extended past its effective width AND the current syllable has NABC, + % shift the current syllable's notes right to prevent overlap. + \ifdim\gre@dimen@nabc@syllable@prevoverflow>0pt\relax% + \global\advance\gre@dimen@notesaligncenter by \gre@dimen@nabc@syllable@prevoverflow\relax% + \setbox\gre@box@syllablenotes=\hbox{% + \kern\gre@dimen@nabc@syllable@prevoverflow + \unhbox\gre@box@syllablenotes + }% + \fi% % now we calculate the begin difference, that is to say \gre@dimen@notesaligncenter - \gre@dimen@textaligncenter \gre@dimen@begindifference=\dimexpr(\gre@dimen@notesaligncenter - \gre@dimen@textaligncenter)\relax % % Now, let's go for something I took years to figure out: we want to align @@ -1235,6 +1278,24 @@ \global\gre@possibleluahyphenafterthissyllablefalse % \fi % \fi% + % Compute NABC right-overflow for the next syllable (replaces extrakern). + % Done here because it needs the final values of enddifference, + % syllablefinalskip, and nextbegindifference (which may have been + % recomputed after adding a hyphen). + \gre@compute@nabc@syllable@overflow + % Further reduce by the natural inter-syllable gap: the syllable-final + % skip and any next-syllable text that extends before the next notes. + \ifdim\gre@dimen@nabc@syllable@overflow>0pt\relax + \global\advance\gre@dimen@nabc@syllable@overflow + by -\gre@skip@syllablefinalskip\relax + \ifdim\gre@skip@nextbegindifference<0pt\relax + \global\advance\gre@dimen@nabc@syllable@overflow + by \gre@skip@nextbegindifference\relax + \fi + \ifdim\gre@dimen@nabc@syllable@overflow<0pt\relax + \global\gre@dimen@nabc@syllable@overflow=0pt\relax + \fi + \fi \ifgre@textcleared% \gre@clearsyllable{note}% \fi% @@ -1264,11 +1325,11 @@ % when debugging we add a zero-width line to mark the syllable bound {\raise 12pt\hbox to 0pt{\rule{0.4pt}{12pt}\hss}}% {}% do nothing if not debugging - #9% we do that instead of \unhbox\Syllablnotes, because it would not set the \localrightbox - % Apply per-syllable NABC kern computed in \gre@syllablenotes. - \ifdim\gre@dimen@nabc@extrakern>0pt\relax - \kern\gre@dimen@nabc@extrakern + % Inter-syllable NABC overflow compensation. + \ifdim\gre@dimen@nabc@syllable@prevoverflow>0pt\relax + \kern\gre@dimen@nabc@syllable@prevoverflow \fi + #9% we do that instead of \unhbox\Syllablnotes, because it would not set the \localrightbox \IfSubStr{\gre@debug}{,notespacing,}% % when debugging we add a zero-width line to mark the syllable bound {\raise 12pt\hbox to 0pt{\rule{0.4pt}{12pt}\hss}}% @@ -1280,11 +1341,10 @@ \gre@check@nabc@needsspacing \ifgre@nabc@needsspacing % NABC visible: render with tight (NABC-only) spacing - \gre@notes@rendernabc@tight{#9}% - % Apply per-syllable NABC kern (computed in \gre@syllablenotes) - \ifdim\gre@dimen@nabc@extrakern>0pt\relax - \kern\gre@dimen@nabc@extrakern + \ifdim\gre@dimen@nabc@syllable@prevoverflow>0pt\relax + \kern\gre@dimen@nabc@syllable@prevoverflow \fi + \gre@notes@rendernabc@tight{#9}% \else % Notes disabled: render NABC without affecting spacing \hbox to 0pt{\gre@notes@rendernabc{#9}\hss}% @@ -1497,10 +1557,13 @@ \global\let\gre@newlinecommon\gre@newlinecommondelayed % \xdef\gre@newlinearg{-1}% \gre@syllablenotes{#9}% + % Bars do not use inter-syllable NABC overflow compensation. + \gre@dimen@nabc@syllable@prevoverflow=0pt\relax% \gre@debugmsg{barspacing}{Width of bar line: \the\wd\gre@box@syllablenotes}% \gre@dimen@notesaligncenter=\dimexpr(\wd\gre@box@syllablenotes / 2)\relax % \gre@dimen@begindifference=\dimexpr(\gre@dimen@notesaligncenter - \gre@dimen@textaligncenter)\relax% \gre@calculate@enddifference{\wd\gre@box@syllablenotes}{\wd\gre@box@syllabletext}{\gre@dimen@textaligncenter}{\gre@dimen@notesaligncenter}{1}% + \gre@compute@nabc@syllable@overflow #5% % if the next glyph has an alteration, check if it is suppressed; % if so, pretend there's no alteration @@ -1575,10 +1638,6 @@ \ifgre@nabc@needsspacing % NABC visible: render with tight (NABC-only) spacing \gre@notes@rendernabc@tight{#9}% - % Apply per-syllable NABC kern (computed in \gre@syllablenotes) - \ifdim\gre@dimen@nabc@extrakern>0pt\relax - \kern\gre@dimen@nabc@extrakern - \fi \else % Notes disabled: render NABC without affecting spacing \hbox to 0pt{\gre@notes@rendernabc{#9}\hss}%