HI again

Sorry for the length of the message...

I'm not answering inline, because I want to give this discussion a (hopefully) new approach that should make the design and layout for `org-latex-fontspec-config' more understandable. Let's go step-by-step in a green-slate approach. Let's forget everything we knew and loved about fonts in LaTeX and org and start from scratch:

1. Typesetting in (Lua)LaTeX is about putting characters in a document.

2. Depending on the markup, we distinguish four families or generic types of fonts: - "main" (the former "roman"): The default family for the document, which unless otherwise specified, is a serif font. - "sans": the family used for text that should be used for test typeset in a Sans Serif - "mono": the family for monospaced test, like, for example, listings, variable names, etc.
  - "math": the family for, for example, equations

3. LaTeX uses a document class, or template, to typeset documents.
  - This template includes a default mapping for the font families above

4. We might need to change this default mapping. For LuaLaTeX, we have the 'fontspec' package to help us changing the document's appearance with a family of commands.

5. `org-latex-fontspec-config' abstracts the mapping desired by the user to an alist mapping the LaTeX font family to the system font to use. We may have some optional properties to fine-tune the appearance. Hence the use of a plist.

Each mapping is then translated into the commands expected by fontspec. For example:

#+BEGIN_SRC emacs-lisp
(setq org-latex-fontspec-config '(("main" :font "TeXGyreSchola")))
#+END_SRC

Is enough to make a document use "TeXGyreSchola" as the main font with no extra fine-tuning. It will be translated to:

#+BEGIN_SRC latex
\setmainfont{TexGyreSchola}
#+END_SRC

and/or:

#+BEGIN_SRC emacs-lisp
(setq org-latex-fontspec-config
  '(("mono" :font "DejaVu Sans Mono" :features "Scale=MatchLowercase"))
#+END_SRC

will typeset the listings (or ~variables~, etc.) with "DejaVu Sans Mono". Additionally, as indicated in the :features property, it will scale this font to make it look approx. the same size as the main font. This will be translated to:

\setmonofont{DejaVu Sans Mono}[Scale=MatchLowercase]

6. There is virtually no font that covers the whole Unicode spectrum and the probability that the font you need does, is fractional. For this case, (Lua)Latex provides the fallback font mechanism. The idea behind it is to add fonts that include the _unsupported *scripts*_ to the typesetting process and let LuaLaTeX manage the details.

The paradigmatic example is typesetting emojis which I'm going to use here. The concept behind it is, translated to plain English:
- Emojis are identified as character in the 'emoji' script in Emacs
- Every time you hit a character in this 'emoji' script, use the fallback font instead of the main, sans, mono or math font of the document. - We introduce a small optimisation here, because we do not add the fallback font for all families, but rather single out the family or families that need the emojis to successfully complete the typesetting,
(Note: Replace emoji with any other script not supported by your font.)

7. With all this in mind, consider the following definition:

#+BEGIN_SRC emacs-lisp
(setq org-latex-fontspec-config
  '(("main" :font "FreeSerif"
     :fallback (("emoji" . "Noto Color Emoji:mode=harf")))))
#+END_SRC

reads, in plain English as:
for text in the the roman (serif) font, please use 'FreeSerif'; and when Emacs identifies that you have to typeset an emoji, add 'Noto Color Emoji' to the fallback font list for 'main'. The emojis need to use the HarfBuzz renderer (which is indicated by adding ':mode=harf' to the font name).

As you see, the whole design uses the
'LaTeX font family or Emacs script -> font w/ characteristics'
principle throughout all steps, which, I hope, gives it a certain degree of consistence.

8. The definition above will result in the following LaTeX code

#+BEGIN_SRC latex
\directlua{
 luaotfload.add_fallback ("fallback_main",{
  "Noto Color Emoji:mode=harf",
 })
}
\setmainfont{FreeSerif}[RawFeature={fallback=fallback_main}]
#+END_SRC

The fact that fallback fonts are expressed as a feature linked to the specific font family makes me put them as a property of the font family.

9. Emacs happens to know about scripts. Thanks to the code provided by J.M. Macías, we can get a list of 'special' scripts used in a document. This list provides the hint we need to guess which font LuaLaTeX will need for unsupported characters. 10. Since we have the scripts, we can use them as the _key_ in the fallback alist. Mapping the script to the full font specification simplifies this list. Once again, please note that the Emacs script name does not appear in the LaTeX code. LuaLaTeX only needs to know that it should try with 'Noto Color Emoji' if a character is not supported by the font used by default. We can supply a larger list that the exporter will then adapt taking the actual scripts contained in the document.
This makes the whole process 'grow as you go' as I pointed out in EmacsConf.

10. Alternatives: If we really need a plist here, if should be like:

("emoji" :font "Noto Color Emoji" :features "mode=harf")

However, when looking for 'fallback fonts' in the Internet, you will find the 'compact' definition used backend the backend and, I feel, this expanded version may introduced confusion. I would rather not go this way.

Best /PA



On 14/12/25 13:16, Ihor Radchenko wrote:
Pedro Andres Aranda Gutierrez <[email protected]> writes:

Are we talking about everything or just about babel configuration?

1. Everything in terms of the way values are defined

Sorry for trying to provide flexibility… this seems to be the source of most of 
your confusion.

I have no problem with flexibility. What I am after is more consistent
value format (without losing flexibility).

(I also wanted to drop redundant [IMHO] options. But since they are not
as redundant as I though, let's keep)

2. Whether things should be consolidated to a single variable is not
   something I worry *too* much about

What is too complex in the following config for fontspec?

((org-mode
  . ((org-latex-multi-lang . "fontspec")
     (org-latex-fontspec-default-features
      . (("Scale" . "MatchLowercase")))
     (org-latex-fontspec-config
      . (("main" :font "TeXGyreSchola"
         :fallback (("emoji" . "AppleColorEmoji:mode=harf")))
         ("sans" :font "TeXGyreHeros")
         ("mono" :font "DejaVu Sans Mono" :features "Scale=MatchLowercase)
         ("math" :font "Stix Two Math"))))))

1. default-features is defined as list of cons, while fontspec features
   is a string while fallback font features is defined with font itself.
   All different! I find this confusing.

OK, we put everything as strings.

2. I am not a big fan of nesting :fallback. Some users tend to get lost
   in complex structures (me too). We had discussions with Emacs devs on
   this topic and the general suggestion is sticking to plists. Either
   directly in variables, or, at least, using some macro helpers to set
   complex variables.

In this case, we really need them. This allows us to grow as we go.
Go back to the MWE sent to the list by a user how wanted to add emojis.

Ok. Let's agree on nested :fallback. I can see how people may prefer
this structure instead of flat.

However, what is still confusing is "emoji" vs "main"/"sans"/"mono"/"math".
They are in the same place, that can be interpreted as the same
meaning. But it is not the same...

What about

(("main" :font "TeXGyreSchola"
   :fallbacks (("AppleColorEmoji:mode=harf" :script "emoji")))


      (org-latex-fontspec-fonts
       . (("TeXGyreSchola" :family "main")
          ("AppleColorEmoji" :family "main" :onchar "emoji" :features 
"mode=harf")
          ("TeXGyreHeros" :family "sans")

For me, fallbacks are subordinate fonts. Having a prop in the fonts that marks 
the fallback fonts makes it easier to identify them in the LaTeX export. I 
don’t identify them as easily with your proposal. Well, actually I’m confused 
by this proposal. Honestly.

Ok. I find flat structure more natural. But that's *me*. I do not find
nested fallback as a true blocker problem.



Reply via email to