Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behaviour of \fontseries #444

Closed
user227621 opened this issue Dec 2, 2020 · 51 comments
Closed

Unexpected behaviour of \fontseries #444

user227621 opened this issue Dec 2, 2020 · 51 comments

Comments

@user227621
Copy link

user227621 commented Dec 2, 2020

Brief outline of the bug

Since LaTeX 2020-02-02 the \fontseries command does not work properly in specific situations when called before \fontfamily.

Consider the following example:

\documentclass{article}
  
\begin{document}

\fontseries{l}\fontfamily{NotoSans-TLF}\selectfont Lorem ipsum

\end{document}

You get:

LaTeX Font Warning: Font shape `OT1/cmr/l/n' undefined
(Font)              using `OT1/cmr/m/n' instead on input line 6.

"Lorem ipsum" should be light, but you get the regular weight. Apparently, a substitution mechanism starts before \selectfont is called: LaTeX tries OT1/cmr/l/n (which doesn't exist), substitutes OT1/cmr/m/n, and finally you get OT1/NotoSans-TLF/m/n.

With LaTeX 2019-10-01 (\RequirePackage[2019/10/01]{latexrelease}) everything works as expected.

The problem seems to affect only font series values for which a pre-defined \DeclareFontSeriesChangeRule exists (see this answer on StackExchange): If you ask for \fontseries{c} or \fontseries{sb}, you get m or b. If you ask for ul, el or ub, you get the requested font series.

The same problem occurs with other fonts.

(First reported on StackExchange. In that discussion, the problem was not really identified as a bug but I thought it might be worth reporting it here because the problem affects compatibility.)

Minimal example showing the bug

\RequirePackage{latexbug}
%\RequirePackage[2019/10/01]{latexrelease} %uncomment to get the light weigt as expected
\documentclass{article}

\begin{document}

\fontseries{l}\fontfamily{NotoSans-TLF}\selectfont Lorem ipsum

\end{document}

Log file (required) and possibly PDF file

This is pdfTeX, Version 3.14159265-2.6-1.40.21 (MiKTeX 20.7) (preloaded format=pdflatex 2020.11.5)  2 DEC 2020 08:53
entering extended mode
**D:/XXX/Dokumente/test.tex
(D:/XXX/Dokumente/test.tex
LaTeX2e <2020-02-02> patch level 5
L3 programming layer <2020-09-03>
("C:\Users\XXX\MiKTeX 2.9\tex/latex/latexbug\latexbug.sty"
Package: latexbug 2020/09/25 v1.0h Bug-classification
)
("C:\Users\XXX\MiKTeX 2.9\tex/latex/base\article.cls"
Document Class: article 2019/12/20 v1.4l Standard LaTeX document class
("C:\Users\XXX\MiKTeX 2.9\tex/latex/base\size10.clo"
File: size10.clo 2019/12/20 v1.4l Standard LaTeX file (size option)
)
\c@part=\count168
\c@section=\count169
\c@subsection=\count170
\c@subsubsection=\count171
\c@paragraph=\count172
\c@subparagraph=\count173
\c@figure=\count174
\c@table=\count175
\abovecaptionskip=\skip47
\belowcaptionskip=\skip48
\bibindent=\dimen134
)
("C:\Users\XXX\MiKTeX 2.9\tex/latex/l3backend\l3backend-pdftex.def"
File: l3backend-pdftex.def 2020-09-01 L3 backend support: PDF output (pdfTeX)
\l__kernel_color_stack_int=\count176
\l__pdf_internal_box=\box45
)
(test.aux)
\openout1 = `test.aux'.

LaTeX Font Info:    Checking defaults for OML/cmm/m/it on input line 5.
LaTeX Font Info:    ... okay on input line 5.
LaTeX Font Info:    Checking defaults for OMS/cmsy/m/n on input line 5.
LaTeX Font Info:    ... okay on input line 5.
LaTeX Font Info:    Checking defaults for OT1/cmr/m/n on input line 5.
LaTeX Font Info:    ... okay on input line 5.
LaTeX Font Info:    Checking defaults for T1/cmr/m/n on input line 5.
LaTeX Font Info:    ... okay on input line 5.
LaTeX Font Info:    Checking defaults for TS1/cmr/m/n on input line 5.
LaTeX Font Info:    ... okay on input line 5.
LaTeX Font Info:    Checking defaults for OMX/cmex/m/n on input line 5.
LaTeX Font Info:    ... okay on input line 5.
LaTeX Font Info:    Checking defaults for U/cmr/m/n on input line 5.
LaTeX Font Info:    ... okay on input line 5.


LaTeX Font Warning: Font shape `OT1/cmr/l/n' undefined
(Font)              using `OT1/cmr/m/n' instead on input line 7.

LaTeX Font Info:    Trying to load font information for OT1+NotoSans-TLF on inp
ut line 7.
("C:\Users\XXX\MiKTeX 2.9\tex/latex/noto\ot1notosans-tlf.fd"
File: OT1NotoSans-TLF.fd 2019/12/09 (autoinst) Font definitions for OT1/NotoSan
s-TLF.
)
LaTeX Font Info:    Font shape `OT1/NotoSans-TLF/m/n' aliased to
(Font)              `OT1/NotoSans-TLF/regular/n' on input line 7.
 [1

{C:/Users/XXX/AppData/Local/MiKTeX/2.9/pdftex/config/pdftex.map}] (test.aux
) ) 
Here is how much of TeX's memory you used:
 1727 strings out of 481199
 45947 string characters out of 2913467
 246698 words of memory out of 3000000
 17400 multiletter control sequences out of 15000+200000
 535969 words of font info for 30 fonts, out of 3000000 for 9000
 1141 hyphenation exceptions out of 8191
 25i,4n,25p,185b,893s stack positions out of 5000i,500n,10000p,200000b,50000s
{C:/Users/XXX/MiKTeX 2.9/fonts/enc/dvips/noto/nto_4lhdnu.enc}<C:/Users/
XXX/MiKTeX 2.9/fonts/type1/google/noto/NotoSans-Regular.pfb><C:/Users/XXX
/MiKTeX 2.9/fonts/type1/public/amsfonts/cm/cmr10.pfb>
Output written on test.pdf (1 page, 18987 bytes).
PDF statistics:
 15 PDF objects out of 1000 (max. 8388607)
 0 named destinations out of 1000 (max. 500000)
 1 words of extra memory for PDF output out of 10000 (max. 10000000)
@FrankMittelbach
Copy link
Member

Workaround (for now) would be to swap \fontseriesand \fontfamily. I'll see if this can be mended or if we have to accept that the more general possibilities offered for font series settings require that you set the the target family first.

@FrankMittelbach
Copy link
Member

Try this (not guaranteed to be right):

\makeatletter

\DeclareRobustCommand\fontseriesforce[1]{\@forced@seriestrue\def\delayed@fseries@adjustment{\edef\f@series{#1}}}
\DeclareRobustCommand\fontseries[1]{\@forced@seriesfalse
  \def\delayed@fseries@adjustment{\merge@font@series{#1}}}

\let\delayed@fseries@adjustment\@empty

\DeclareRobustCommand\selectfont
        {%
    \ifx\f@linespread\baselinestretch \else
      \set@fontsize\baselinestretch\f@size\f@baselineskip \fi
    \delayed@fseries@adjustment
    \let\delayed@fseries@adjustment\@empty
    \xdef\font@name{%
      \csname\curr@fontshape/\f@size\endcsname}%
    \pickup@font
    \font@name
    \size@update
    \enc@update
    }
\makeatother

@FrankMittelbach FrankMittelbach added this to Pool (unscheduled issues) in upcoming LaTeX2e releases via automation Dec 2, 2020
@user227621
Copy link
Author

@FrankMittelbach I just noticed that \fontshape is implemented in much the same way as \fontseries. I have not checked that yet with examples, but maybe the \fontshape command suffers from a similar problem.

By the way: the current version of source2e.pdf is full of undefined references: When I search the PDF for "is documented on page ??" I get 1186 occurrences...

@FrankMittelbach
Copy link
Member

FrankMittelbach commented Dec 2, 2020

I have not checked that yet with examples, but maybe the \fontshape command suffers from a similar problem.

I guess you are "technically" right, although chances that it actually fails are rather slim compared to the series situation as the available fontshapes are fairly uniform. But yes, that merging should be delayed in exactly the same way. Thanks for noticing.

@FrankMittelbach
Copy link
Member

By the way: the current version of source2e.pdf is full of undefined references: When I search the PDF for "is documented on page ??" I get 1186 occurrences...

true, this is due to us starting to use l3doc for the sources, but a lot of the documented code is in old ltxdoc style. So we end up with that warning whenever there is no \begin{function} ... providing a users level documentation. Maybe for now we should simply suppress those (much better than a 1000 links to nowhere.

@josephwright
Copy link
Member

@FrankMittelbach I'd noticed something similar when preparing my PR: I think we should agree on an approach to whether to use l3doc or ltxdoc or ...

@FrankMittelbach
Copy link
Member

@aminophen do you see a dependency to your code there?

@FrankMittelbach
Copy link
Member

@josephwright I opened a separate issue for the documentation woes (#445)

@aminophen
Copy link
Contributor

@FrankMittelbach Yes I will adjust platex code after yours is settled.

@aminophen
Copy link
Contributor

aminophen commented Dec 2, 2020

@FrankMittelbach And, you will have to report to everysel package author, as it redefines \selectfont

@FrankMittelbach
Copy link
Member

@FrankMittelbach And, you will have to report to everysel package author, as it redefines \selectfont

good point, but that is unmaintained bascially, ok that then would need to be handeled by firstaid for the moment

@aminophen
Copy link
Contributor

@FrankMittelbach Hmm CJK package also does ...

@FrankMittelbach
Copy link
Member

looks like the hook provided by everysel should be offered by the kernel and then used by tracefnt, CJK and everysel changed to just use the hook instead of installing it and changing the other packages.

@u-fischer
Copy link
Member

@FrankMittelbach if everysel should be changed there should be no need to use firstaid, we can either take it or arrange that someone makes an update.

@FrankMittelbach
Copy link
Member

@u-fischer if we don't delay the fix to the next release there is a need for firstaid for at least CJK and so we can then do it for everysel as well and it can then be updated later (which makes things much simpler).

But I'm guess it is probably better to just schedule the change for the spring release. The problem is not so severe that one couldn't justify delaying the fix especially as a work around is available.

@aminophen
Copy link
Contributor

aminophen commented Dec 2, 2020

@FrankMittelbach Agreed, it would be great if the kernel incorporates everysel functionality.

FYI, platex redefines \selectfont broadly, to enable changing all of Japanese-yoko, Japanese-tate and Latin fonts at the same time. Note that \size@update should be at last, because we need to adjust base line shift amount between Japanese and Latin fonts here after font size is updated.

https://github.com/texjporg/platex/blob/master/plcore.ltx#L584-L638

https://github.com/texjporg/platex/blob/master/ptrace.sty#L22-L78

@FrankMittelbach
Copy link
Member

FrankMittelbach commented Dec 2, 2020 via email

@user227621
Copy link
Author

@FrankMittelbach I just noticed the following: There is a command \if@forced@series which is used inside \fontseries/\fontseriesforce. Apparently, this command has been introduced while fixing #277. However, there is no similar command \if@forced@shape inside \fontshape/\fontshapeforce. Is it possible to get a problem similar to #277 when changing the font shape?

@aminophen
Copy link
Contributor

(platex) So where else would you need a hook? At the start of the command?

I don't think hook mechanism is useful here; there is no way to remove the existing \size@update in the original and insert it afterward. You can update \selectfont without thinking about our platex, and I will update platex to be in sync with yours.

@FrankMittelbach
Copy link
Member

@FrankMittelbach I just noticed the following: There is a command \if@forced@series which is used inside \fontseries/\fontseriesforce. Apparently, this command has been introduced while fixing #277. However, there is no similar command \if@forced@shape inside \fontshape/\fontshapeforce. Is it possible to get a problem similar to #277 when changing the font shape?

I think it is correct. The high-level font command \bfseries and \mdseries change the series sometimes when you change the font family, e.g., so when you ask for "give me bold" they might select "bx" in LMR while they select "b" in Noto. The forced series bypasses that mechanism and really uses what you ask for. A comparable mechanism doesn't exist for shapes (they do not vary their meaning by font family) so here is the "forced" just means use whatever I tell you. But you are right it needs to be checked if delaying the series handling might have an effect on this mechanism.

@user227621
Copy link
Author

user227621 commented Dec 2, 2020

@FrankMittelbach One also has to keep in mind that series and shape are interdependent. If both the series handling and the shape handling are delayed one has to make sure that they are somehow evaluated together.

If e. g. the series is always handled first and the combination "requested series"/"current shape" does not exist (but "requested series"/"requested shape" does) you get an unwanted substitution.

@user227621
Copy link
Author

user227621 commented Dec 11, 2020

true, but you could argue that this is only because such a rule is missing, could you not?

@FrankMittelbach There are no \DeclareFontSeriesChangeRules where the second argument sets both weight and width (like bc). Of course such rules would improve the substitution, but then you would need a lot of new rules... I don't know if it's worth the effort. (And such rules are only useful in case a substitution occurs. If no substition is necessery, you get the same result with or without such additional rules.)

FrankMittelbach added a commit that referenced this issue Dec 22, 2020
@FrankMittelbach FrankMittelbach moved this from In progress to Done in dev in upcoming LaTeX2e releases Dec 22, 2020
@FrankMittelbach FrankMittelbach added the fixed in dev Fixed in development branch, not in stable release label Jan 6, 2021
@user227621
Copy link
Author

@FrankMittelbach

The proposed changes work fine in themselves, but some commands which use \edef\f@series{...} or \edef\f@shape{...} directly no longer work properly. It may be necessary to evaluate all occurences of \(e)def\f@series{...} and \(e)def\f@shape{...} in the kernel in order to track down other possible problems.

(For the following examples, I have collected the changes in a file called font-selection-fixes.tex:

\makeatletter

% ---------- new definition of \usefont ----------

\DeclareRobustCommand\usefont[4]{\fontencoding{#1}%
  \edef\f@family{#2}%
  \set@target@series{#3}%
  \edef\f@shape{#4}\selectfont
  \ignorespaces}

% ---------- new command \delayed@f@adjustment ----------

\let\delayed@f@adjustment\@empty

% ---------- font series handling ---------

\DeclareRobustCommand\fontseries[1]{\@forced@seriesfalse
  \expandafter\def\expandafter\delayed@f@adjustment\expandafter
    {\delayed@f@adjustment\delayed@merge@font@series{#1}}}

\DeclareRobustCommand\fontseriesforce[1]{\@forced@seriestrue
  \expandafter\def\expandafter\delayed@f@adjustment\expandafter
    {\delayed@f@adjustment\edef\f@series{#1}}}

\def\merge@font@series@#1#2#3\@nil{%
  \def\reserved@a{#3}%
  \ifx\reserved@a\@empty
    \set@target@series{#2}%
  \else
    \edef\reserved@a{\f@encoding /\f@family /#1/\f@shape}%
     \ifcsname \reserved@a \endcsname
       \set@target@series{#1}%
    \else
       \ifcsname \f@encoding /\f@family /#2/\f@shape \endcsname
         \set@target@series{#2}%
         \@font@shape@subst@warning
       \else
         \set@target@series{#3}%
         \@font@shape@subst@warning
       \fi
    \fi
  \fi
}

\def\merge@font@series@without@substitution#1{%
  \expandafter\expandafter\expandafter
  \merge@font@series@without@substitution@
    \csname series@\f@series @#1\endcsname
    {#1}%
    \@nil
}

\def\merge@font@series@without@substitution@#1#2#3\@nil{%
  \def\reserved@a{#3}%
  \ifx\reserved@a\@empty
    \set@target@series{#2}%
  \else
    \set@target@series{#1}%
  \fi
}

\let\delayed@merge@font@series\merge@font@series@without@substitution

% ---------- new definition of \maybe@load@fontshape ----------

\def\maybe@load@fontshape{%
  \begingroup
    \let \typeout \@font@info
    \try@load@fontshape
  \endgroup}


% ---------- font shape handling ----------

\DeclareRobustCommand\fontshape[1]
  {\expandafter\def\expandafter\delayed@f@adjustment\expandafter
    {\delayed@f@adjustment\delayed@merge@font@shape{#1}}}

\DeclareRobustCommand\fontshapeforce[1]
  {\expandafter\def\expandafter\delayed@f@adjustment\expandafter
    {\delayed@f@adjustment\edef\f@shape{#1}}}

\def\merge@font@shape@#1#2#3\@nil{%
  \def\reserved@a{#3}%
  \ifx\reserved@a\@empty
    \edef\f@shape{#2}%
  \else
    \edef\reserved@a{\f@encoding /\f@family /\f@series/#1}%
    \ifcsname \reserved@a\endcsname
       \edef\f@shape{#1}%
    \else
       \ifcsname \f@encoding /\f@family /\f@series/#2\endcsname
         \edef\f@shape{#2}%
         \@font@shape@subst@warning
       \else
         \edef\f@shape{#3}%
         \@font@shape@subst@warning
       \fi
    \fi
  \fi
}

\def\merge@font@shape@without@substitution#1{%
  \expandafter\expandafter\expandafter
  \merge@font@shape@without@substitution@
    \csname shape@\f@shape @#1\endcsname
    {#1}%
    \@nil
}

\def\merge@font@shape@without@substitution@#1#2#3\@nil{%
  \def\reserved@a{#3}%
  \ifx\reserved@a\@empty
    \edef\f@shape{#2}%
  \else
    \edef\f@shape{#1}%
  \fi
}

\let\delayed@merge@font@shape\merge@font@shape@without@substitution

% ---------- new definition of \selectfont --------------------

\DeclareRobustCommand\selectfont{%
  \ifx\f@linespread\baselinestretch \else
    \set@fontsize\baselinestretch\f@size\f@baselineskip \fi
  \ifx\delayed@f@adjustment\@empty
  \else
    \let\f@shape@saved\f@shape
    \let\f@series@saved\f@series 
    \delayed@f@adjustment
    \maybe@load@fontshape
    \ifcsname \f@encoding/\f@family/\f@series/\f@shape \endcsname
    \else
      \let\f@shape\f@shape@saved
      \let\f@series\f@series@saved
      \let\delayed@merge@font@shape\merge@font@shape
      \let\delayed@merge@font@series\merge@font@series
      \delayed@f@adjustment
      \let\delayed@merge@font@shape\merge@font@shape@without@substitution
      \let\delayed@merge@font@series\merge@font@series@without@substitution
    \fi
    \let\delayed@f@adjustment\@empty
  \fi
  \xdef\font@name{%
    \csname\curr@fontshape/\f@size\endcsname}%
  \pickup@font
  \font@name
%  \UseHook{selectfont}%
  \size@update
  \enc@update
}

\makeatother

)

Issue 1
(This issue is caused by delaying the series/shape handling.) If there is a \fontseries{...} without a following \selectfont before \rmfamily or \sffamily or \ttfamily, the delayed actions overwrite the series determined by \rmfamily (resp. \sffamily or \ttfamily):

\documentclass{article}

\input{font-selection-fixes.tex}
 
\begin{document}

\fontseries{b}\rmfamily Lorem ipsum

\end{document}

"Lorem ipsum" is b, but it would be bx with LaTeX 2020-10-01: \rmfamily detects that it is called in a "bold context" and "bold" means bx for rm. With LaTeX 2019-10-01, however, you get b.

Issue 2
(This issue is caused by delaying the series/shape handling.) If there is a \fontseries{...} or \fontshape{...} without a following \selectfont before \normalfont or \usefont, the delayed actions overwrite the series/shape requested by \normalfont (resp. \usefont):

\documentclass{article}

\input{font-selection-fixes.tex}
 
\begin{document}

{\fontseries{b}\normalfont Lorem ipsum}%gives b

{\fontshape{it}\normalfont Lorem ipsum}%gives it

{\fontseries{b}\usefont{OT1}{cmr}{m}{n} Lorem ipsum}%gives b

{\fontshape{it}\usefont{OT1}{cmr}{m}{n} Lorem ipsum}%gives it

\end{document}

Without the delayed font selection handling, all four "Lorem ipsum" are cmr/m/n.

Issue 3
(This issue is not caused by delaying the series/shape handling. It has existed since the introduction of \if@forced@series in LaTeX 2020-02-02 PL 3. I mention this because it is kind of related to issue 2. Maybe a seperate issue should be opened.) \usefont and \normalfont do not change \if@forced@series. If there is a \fontseriesforce{...} before \usefont or \normalfont, the "forced" state persists, but now applies to the series used by \usefont or \normalfont. See the following example (explanations below):

\documentclass{article}

\input{font-selection-fixes.tex}

\renewcommand{\sfdefault}{NotoSans-TLF}
\DeclareFontSeriesDefault[sf]{bf}{sb}
 
\begin{document}

{
\fontseriesforce{m}\selectfont Lorem ipsum%forced

\usefont{OT1}{NotoSans-TLF}{sb}{n} Lorem ipsum

\rmfamily Lorem ipsum%fails: you get m
}

{
\fontseries{m}\selectfont Lorem ipsum%not forced

\usefont{OT1}{NotoSans-TLF}{sb}{n} Lorem ipsum

\rmfamily Lorem ipsum%works: you get bx
}

\end{document}

In the example, Noto Sans is set as sans serif (\renewcommand{\sfdefault}{NotoSans-TLF}) and bold means sb for sans serif (\DeclareFontSeriesDefault[sf]{bf}{sb}). Now

\fontseriesforce{m}\selectfont Lorem ipsum%forced

\usefont{OT1}{NotoSans-TLF}{sb}{n} Lorem ipsum

\rmfamily Lorem ipsum%fails: you get m

fails, because the "forced" state applies to sb after the \usefont command and cmr/sb/n does not exist. However

\fontseries{m}\selectfont Lorem ipsum%not forced

\usefont{OT1}{NotoSans-TLF}{sb}{n} Lorem ipsum

\rmfamily Lorem ipsum%works: you get bx

works, because \rmfamily detects that it is called in a "bold context" and bold is bx for rm.

It is probably possible to cause a similar problem with \normalfont if you set \seriesdefault to a non-standard value.

Remark
I think the interface for font series selection has become a bit inconsistent. There are at least four ways to set a font series:

  • \fontseries{...}: merges font series, drops a legacy m (e. g. convert mc to c), sets \if@forced@seriesfalse, delayed execution
  • \fontseriesforce{...}: overwrites font series, does not drop a legacy m, sets \if@forced@seriestrue, delayed execution
  • \edef\f@series{...}: overwrites font series, does not drop a legacy m, does not set \if@forced@series, immediate execution
  • \set@target@series{...}: overwrites font series, drops a legacy m, does not set \if@forced@series, immediate execution

Can that be done more systematically?

For \usefont or \normalfont, I think "overwrites font series, drops a legacy m, sets \if@forced@seriesfalse" would be good, but currently there is no such command.

(I'm sorry, this comment has become very long.)

@FrankMittelbach FrankMittelbach moved this from Done in dev to In progress in upcoming LaTeX2e releases Jan 10, 2021
@FrankMittelbach FrankMittelbach removed the fixed in dev Fixed in development branch, not in stable release label Jan 10, 2021
@user227621
Copy link
Author

user227621 commented Jan 11, 2021

@FrankMittelbach

Perhaps too many commands assume that \f@series ( resp. \f@shape) contains the last requested series (resp. shape) and then get confused by the delayed actions.

Hence another proposal...

The idea is the following: It is not necessary to delay the \merge@font@series@without@substitution and \merge@font@shape@without@substitution actions because these commands do not test whether particular family/series/shape combinations exist. So these actions could be executed immediately. \delayed@f@adjustment would then only be used for the \merge@font@series and \merge@font@shape actions and only if substitutions are necessary (i. e. if the the requested family/series/shape combination does not exist).


\documentclass{article}

\makeatletter

% ---------- new definition of \usefont ----------

\DeclareRobustCommand\usefont[4]{\fontencoding{#1}%
  \edef\f@family{#2}%
  \set@target@series{#3}%
  \edef\f@shape{#4}\selectfont
  \ignorespaces}

% ---------- new command \delayed@f@adjustment ----------

\let\delayed@f@adjustment\@empty

% ---------- font series handling ---------

\DeclareRobustCommand\fontseries[1]{%
  \@forced@seriesfalse
  \merge@font@series@without@substitution{#1}%
  \expandafter\def\expandafter\delayed@f@adjustment\expandafter
    {\delayed@f@adjustment\merge@font@series{#1}}%
}

\DeclareRobustCommand\fontseriesforce[1]{%
  \@forced@seriestrue
  \edef\f@series{#1}%
  \expandafter\def\expandafter\delayed@f@adjustment\expandafter
    {\delayed@f@adjustment\edef\f@series{#1}}%
}

\def\merge@font@series@#1#2#3\@nil{%
  \def\reserved@a{#3}%
  \ifx\reserved@a\@empty
    \set@target@series{#2}%
  \else
    \edef\reserved@a{\f@encoding /\f@family /#1/\f@shape}%
     \ifcsname \reserved@a \endcsname
       \set@target@series{#1}%
    \else
       \ifcsname \f@encoding /\f@family /#2/\f@shape \endcsname
         \set@target@series{#2}%
         \@font@shape@subst@warning
       \else
         \set@target@series{#3}%
         \@font@shape@subst@warning
       \fi
    \fi
  \fi
}

\def\merge@font@series@without@substitution#1{%
  \expandafter\expandafter\expandafter
  \merge@font@series@without@substitution@
    \csname series@\f@series @#1\endcsname
    {#1}%
    \@nil
}

\def\merge@font@series@without@substitution@#1#2#3\@nil{%
  \def\reserved@a{#3}%
  \ifx\reserved@a\@empty
    \set@target@series{#2}%
  \else
    \set@target@series{#1}%
  \fi
}

% ---------- new definition of \maybe@load@fontshape ----------

\def\maybe@load@fontshape{%
  \begingroup
    \let \typeout \@font@info
    \try@load@fontshape
  \endgroup}


% ---------- font shape handling ----------

\DeclareRobustCommand\fontshape[1]{%
  \merge@font@shape@without@substitution{#1}%
  \expandafter\def\expandafter\delayed@f@adjustment\expandafter
    {\delayed@f@adjustment\merge@font@shape{#1}}%
}

\DeclareRobustCommand\fontshapeforce[1]{%
  \edef\f@shape{#1}%
  \expandafter\def\expandafter\delayed@f@adjustment\expandafter
    {\delayed@f@adjustment\edef\f@shape{#1}}%
}

\def\merge@font@shape@#1#2#3\@nil{%
  \def\reserved@a{#3}%
  \ifx\reserved@a\@empty
    \edef\f@shape{#2}%
  \else
    \edef\reserved@a{\f@encoding /\f@family /\f@series/#1}%
    \ifcsname \reserved@a\endcsname
       \edef\f@shape{#1}%
    \else
       \ifcsname \f@encoding /\f@family /\f@series/#2\endcsname
         \edef\f@shape{#2}%
         \@font@shape@subst@warning
       \else
         \edef\f@shape{#3}%
         \@font@shape@subst@warning
       \fi
    \fi
  \fi
}

\def\merge@font@shape@without@substitution#1{%
  \expandafter\expandafter\expandafter
  \merge@font@shape@without@substitution@
    \csname shape@\f@shape @#1\endcsname
    {#1}%
    \@nil
}

\def\merge@font@shape@without@substitution@#1#2#3\@nil{%
  \def\reserved@a{#3}%
  \ifx\reserved@a\@empty
    \edef\f@shape{#2}%
  \else
    \edef\f@shape{#1}%
  \fi
}

% ---------- new definition of \selectfont --------------------

\DeclareRobustCommand\selectfont{%
  \ifx\f@linespread\baselinestretch \else
    \set@fontsize\baselinestretch\f@size\f@baselineskip \fi
  \maybe@load@fontshape
  \ifcsname \f@encoding/\f@family/\f@series/\f@shape \endcsname
  \else
    \ifx\delayed@f@adjustment\@empty
    \else
      \let\f@shape\f@shape@saved
      \let\f@series\f@series@saved
      \delayed@f@adjustment
    \fi
  \fi
  \let\delayed@f@adjustment\@empty
  \xdef\font@name{%
    \csname\curr@fontshape/\f@size\endcsname}%
  \pickup@font
  \font@name
%  \UseHook{selectfont}%
  \size@update
  \enc@update
  \let\f@shape@saved\f@shape
  \let\f@series@saved\f@series 
}

\makeatother
 
\begin{document}

A lot of tests:

{\fontfamily{NotoSans-TLF}\selectfont Lorem ipsum}

{\fontseries{l}\fontfamily{NotoSans-TLF}\selectfont Lorem ipsum}

{\fontfamily{lmss}\fontseries{c}\fontseries{sb}\selectfont Lorem ipsum}

{\fontfamily{lmss}\fontseries{sb}\fontseries{c}\selectfont Lorem ipsum}

{\fontfamily{lmss}\fontseries{sbc}\selectfont Lorem ipsum}

{\fontfamily{NotoSans-TLF}\fontseries{b}\fontseries{c}\selectfont Lorem ipsum}

{\fontfamily{NotoSans-TLF}\fontseries{bc}\selectfont Lorem ipsum}

{\fontfamily{NotoSans-TLF}\fontshape{sc}\fontshape{it}\selectfont Lorem ipsum}

{\fontfamily{NotoSans-TLF}\fontshape{scit}\selectfont Lorem ipsum}

{
\fontfamily{lmr}\fontshape{sc}\selectfont Lorem ipsum

\fontfamily{lmtt}\fontseries{l}\fontshape{n}\selectfont Lorem ipsum
}

{
\fontseries{bx}\fontshape{it}\selectfont Lorem ipsum

\fontseries{c}\selectfont Lorem ipsum%reasonable substitution
}

{\fontseries{b}\rmfamily Lorem ipsum}

{\fontseries{b}\normalfont Lorem ipsum}

{\fontshape{it}\normalfont Lorem ipsum}

{\fontseries{b}\usefont{OT1}{cmr}{m}{n} Lorem ipsum}

{\fontshape{it}\usefont{OT1}{cmr}{m}{n} Lorem ipsum}

\end{document}
  • \fontseries, \fontseriesforce, \fontshape, \fontshapeforce now immediatly change \f@series ( resp. \f@shape) and additionally add an entry to \delayed@f@adjustment (which is used only in case substituions are necessary)
  • \selectfont now first checks if the requested family/series/shape combination exists. If it does, no furter adjustments are necessary. If it does not exist and \delayed@f@adjustment is not empty, \f@series and \f@shape are reset to the values they had after the previous invocation of \selectfont and \delayed@f@adjustment is executed.
  • At the end of \selectfont, \f@series and \f@shape are saved (\f@series@saved and \f@shape@saved) so that they can reset at the next invocation of \selectfont if substitutions are necesary.

This proposal should fix some, but not all, problems. The following scenario will not work well: If the series is changed directly via \edef\f@series{...}, then a corresponding entry in \delayed@f@adjustment will be missing. I. e. if the series is changed directly via \edef\f@series{...} and the family/series/shape combiation does not exist, you might get a strange substitution (due to the missing entry). Same for the shape.

So, issues 1 and 2 I mentioned in my previous comment are partly fixed (substitutions maybe not ideal). To fully fix them, \normalfont and \usefont probably need to be modified.

Issue 3 is kind of a separate problem and not fixed by this proposal.

@mrpiggi
Copy link

mrpiggi commented Jan 21, 2021

looks like the hook provided by everysel should be offered by the kernel and then used by tracefnt, CJK and everysel changed to just use the hook instead of installing it and changing the other packages.

Is there any reason, why package tracefnt in release 2021-05-01 still (re)declares \selectfont although it uses now hook selectfont? Is it related to the <debug> guard?

@FrankMittelbach
Copy link
Member

@mrpiggi I considered changing that but that would have made the rollback code much more complicated (and that is error prone enough as seen by #479 (which is a bummer)).

@mrpiggi
Copy link

mrpiggi commented Jan 22, 2021

got it

@stale stale bot added the stale label Mar 26, 2021
@user227621
Copy link
Author

Hi stale-bot :-)

Is there any news on this issue? The implementation currently used in latex-dev still seems to have some problems (see this comment and this comment above). I'm not sure if it's a good idea to use this implementation in the final release.

@stale stale bot removed the stale label Mar 26, 2021
@PhelypeOleinik
Copy link
Member

@user227621 Don't worry about the bot: it just nudges on issues that didn't have recent activity. It doesn't mean that the issue is forgotten (on the contrary). Frank already labelled this as a bug and set a milestone to fix this for the May release, so if things go according to plan it will be fixed by then.

@FrankMittelbach
Copy link
Member

@user227621

Issue 1
(This issue is caused by delaying the series/shape handling.) If there is a \fontseries{...} without a following \selectfont before \rmfamily or \sffamily or \ttfamily, the delayed actions overwrite the series determined by \rmfamily (resp. \sffamily or \ttfamily):

I consider this a non-issue. I think it is acceptable that \fontseries{b} \rmfamily has the same effect as \rmfamily\fontseries{b}\selectfont in fact I think that is more consistent than anything else. A meta family like \rmfamily is not setting the series it is adjusting there series when it continues in a mold or in a medium context.

Issue 2
(This issue is caused by delaying the series/shape handling.) If there is a \fontseries{...} or \fontshape{...} without a following \selectfont before \normalfont or \usefont, the delayed actions overwrite the series/shape requested by \normalfont (resp. \usefont):

That is indeed plain wrong: \usefont and \normalfontare supposed to switch to a well defined face and not depend on surrounding conditions.

Issue 3
(This issue is not caused by delaying the series/shape handling. It has existed since the introduction of \if@forced@series in LaTeX 2020-02-02 PL 3. I mention this because it is kind of related to issue 2. Maybe a seperate issue should be opened.) \usefont and \normalfont do not change \if@forced@series. If there is a \fontseriesforce{...} before \usefont or \normalfont, the "forced" state persists, but now applies to the series used by \usefont or \normalfont. See the following example (explanations below):

again a mistake, I agree. the force should always be reset by \selectfont not by the next \fontseries

but in your examples you switch to "sb" and then claim that this is recognized as being a bold context. It isn't and never was unless you change the Noto font to have a bfdefault of sb. What is recognized is the bfdefault of the current meta family and the overall bfdefault in other words "bx" or "b"

@latex3 latex3 deleted a comment from stale bot Apr 26, 2021
@FrankMittelbach
Copy link
Member

Perhaps too many commands assume that \f@series ( resp. \f@shape) contains the last requested series (resp. shape) and then get confused by the delayed actions.

Hence another proposal...

The idea is the following: It is not necessary to delay the \merge@font@series@without@substitution and \merge@font@shape@without@substitution actions because these commands do not test whether particular family/series/shape combinations exist. So these actions could be executed immediately. \delayed@f@adjustment would then only be used for the \merge@font@series and \merge@font@shape actions and only if substitutions are necessary (i. e. if the the requested family/series/shape combination does not exist).

I'm a bit reluctant to make a change like that, that means you sometimes get delays and sometimes not and that even on the documentlevel commands such as \fontseries or \fontseriesforced etc, so my plan is to just fix issues 2 and 3 above (as they are clearly bugs but otherwise leave the implementation as it is now.

FrankMittelbach added a commit that referenced this issue Apr 26, 2021
@FrankMittelbach FrankMittelbach moved this from In progress to Done in dev in upcoming LaTeX2e releases Apr 26, 2021
@FrankMittelbach FrankMittelbach added the fixed in dev Fixed in development branch, not in stable release label Apr 26, 2021
@user227621
Copy link
Author

@FrankMittelbach

Issue 1
(This issue is caused by delaying the series/shape handling.) If there is a \fontseries{...} without a following \selectfont before \rmfamily or \sffamily or \ttfamily, the delayed actions overwrite the series determined by \rmfamily (resp. \sffamily or \ttfamily):

I consider this a non-issue. I think it is acceptable that \fontseries{b} \rmfamily has the same effect as \rmfamily\fontseries{b}\selectfont in fact I think that is more consistent than anything else. A meta family like \rmfamily is not setting the series it is adjusting there series when it continues in a mold or in a medium context.

Yes, I agree.

Issue 3
(This issue is not caused by delaying the series/shape handling. It has existed since the introduction of \if@forced@series in LaTeX 2020-02-02 PL 3. I mention this because it is kind of related to issue 2. Maybe a seperate issue should be opened.) \usefont and \normalfont do not change \if@forced@series. If there is a \fontseriesforce{...} before \usefont or \normalfont, the "forced" state persists, but now applies to the series used by \usefont or \normalfont. See the following example (explanations below):

again a mistake, I agree. the force should always be reset by \selectfont not by the next \fontseries

but in your examples you switch to "sb" and then claim that this is recognized as being a bold context. It isn't and never was unless you change the Noto font to have a bfdefault of sb.

But in my example, I did just that: \DeclareFontSeriesDefault[sf]{bf}{sb}. The code was

\documentclass{article}

\input{font-selection-fixes.tex}

\renewcommand{\sfdefault}{NotoSans-TLF}
\DeclareFontSeriesDefault[sf]{bf}{sb}
 
\begin{document}

{
\fontseriesforce{m}\selectfont Lorem ipsum%forced

\usefont{OT1}{NotoSans-TLF}{sb}{n} Lorem ipsum

\rmfamily Lorem ipsum%fails: you get m
}

{
\fontseries{m}\selectfont Lorem ipsum%not forced

\usefont{OT1}{NotoSans-TLF}{sb}{n} Lorem ipsum

\rmfamily Lorem ipsum%works: you get bx
}

\end{document}

When \rmfamily is called in the second code chunk, the current family is NotoSans-TLF, which is \sfdefault, and the current series is sb, which is \bfseries@sf. So this is a "bold context" and \rmfamily therefore chooses \bfseries@rm as series. Or am I getting something wrong?


On this occasion I discovered another inconsistency in the 2020/10/01 implementation that has now been fixed by delaying the series handling: If there is a \fontseries{<requested series>} without a following \selectfont before \rmfamily or \sffamily or \ttfamily and <requested series> is the medium or bold series of the previous meta family, you may end up with another series.

Consider the following example (compile with LaTeX 2020/10/01):

\documentclass{article}

\renewcommand{\rmdefault}{NotoSerif-TLF}
\renewcommand{\sfdefault}{NotoSans-TLF}
\DeclareFontSeriesDefault[rm]{bf}{sb}
 
\begin{document}

Lorem ipsum \fontseries{sb}\sffamily Lorem ipsum

\end{document}

The second "Lorem ipsum" is b, though you would expect sb. This is because at the point where \sffamily is called, the current family is NotoSerif-TLF (which is \rmfamily) and the current series is sb (which is \bfseries@rm). So this is a "bold context" and \sffamily therefore chooses \bfseries@sf as series, which is b.

@FrankMittelbach
Copy link
Member

But in my example, I did just that: \DeclareFontSeriesDefault[sf]{bf}{sb}. The code was

so you did. Not sure why I didn't copied that over when I made my own example file with all three issues.

upcoming LaTeX2e releases automation moved this from Done in dev to Done May 4, 2021
@FrankMittelbach FrankMittelbach removed the fixed in dev Fixed in development branch, not in stable release label Jun 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

No branches or pull requests

7 participants