quarto-dev / quarto-dev/quarto-cli
Typst brand fonts never download a bold-italic face from Google Fonts
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 6k
- Forks
- 458
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 41
Description
I have:
- searched the issue tracker for similar issues
- installed the latest version of Quarto CLI
- formatted my issue following the Bug Reports guide
Bug description
For format: typst, Quarto downloads the Google fonts named in the brand
into .quarto/typst/fonts/. When a font entry gives both a style and a
weight, the two never combine. Quarto downloads the weights at normal style
and weight 400 at each style. It never downloads a bold-italic face.
The result is silent. Typst has no face to use, so #strong[... #emph[x] ...]
shows italic at regular weight. A brand that asks for a bold-italic heading
gets the same. Nothing in the render log says a face is missing.
The cause is the URL that Quarto builds for the Google Fonts API. In
src/command/render/pandoc.ts#L1548-L1560,
the family, the style list, and the weight list are joined with :
const parts = [family!];
if (style) {
style = Array.isArray(style) ? style : [style];
parts.push(style.join(","));
}
if (weight) {
weight = Array.isArray(weight) ? weight : [weight];
parts.push(weight.join(","));
}
const response = await fetch(
`${base_urls[source]}?family=${parts.join(":")}`,
);
For style: [normal, italic] and weight: [400, 700] this gives:
https://fonts.googleapis.com/css?family=Atkinson Hyperlegible:normal,italic:400,700
That is not valid syntax for the v1 CSS API. The v1 API expects one
comma-separated variant list, in which a weight and a style are one token
(700italic). The API splits the string on commas and reads each part as a
separate variant. The parts here are normal, italic:400 and 700, so it
returns normal 400, italic 400 and normal 700.
Two faults follow from this:
- A weight and a style never combine, so bold italic is unreachable.
- The first weight in the list is joined to the last style and is lost. For
style: italicandweight: [700, 900]the tokenitalic:700returns
italic 400, and only 900 arrives as a weight.
HTML is not affected. HTML uses a separate code path in
src/core/sass/brand.ts#L119-L153,
which builds a v2 (css2) URL with correct ital,wght@ pairs. For the same
brand it emits
css2?family=Atkinson+Hyperlegible:ital,wght@0,700;1,700, and that URL does
serve italic 700.
Steps to reproduce
Render this document. The brand is in the document YAML, so no _brand.yml
file is necessary. It asks for two weights at two styles.
---
title: "Bold italic"
format: typst
brand:
typography:
fonts:
- family: Atkinson Hyperlegible
source: google
weight: [400, 700]
style: [normal, italic]
base: Atkinson Hyperlegible
---
Normal. *Italic.* **Bold.** ***Bold italic.***
Then ask Typst which variants it found in the font cache:
quarto typst fonts --variants --font-path .quarto/typst/fonts
Actual behavior
No bold-italic face is in the cache:
Atkinson Hyperlegible
├ .../9Bt43C1KxNDXMspQ1lPyU89-1h6ONRlW45G056IqUwU.ttf
│ Style: Italic, Weight: 400, Stretch: 100%
├ .../9Bt73C1KxNDXMspQ1lPyU89-1h6ONRlW45G8Wbc9dCWK.ttf
│ Style: Normal, Weight: 700, Stretch: 100%
└ .../9Bt23C1KxNDXMspQ1lPyU89-1h6ONRlW45G04pIt.ttf
Style: Normal, Weight: 400, Stretch: 100%
Three faces arrive out of the four requested combinations, and italic 700 is
the one that never arrives. All four exist for this family. The render gives
no warning, so ***Bold italic.*** shows italic at regular weight.
The result is the same when the brand asks for the bold-italic face alone.
With weight: 700 and style: italic, one face arrives, and it is
Style: Italic, Weight: 400.
Two separate fonts: entries for the same family give the same result. A
_brand.yml file gives the same result as the document YAML above: both
produce an identical .typ file.
Google does serve the face. This command returns italic 700:
curl -s "https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400;1,700"
Expected behavior
Quarto downloads a face for each combination of the requested weights and
styles that exist for the family. For the document above,
quarto typst fonts --variants also reports Style: Italic, Weight: 700.
If a combination does not exist for the family, Quarto says so in the render
log, as it already does for an unusable font format.
Your environment
- IDE: Positron 1.130.0
- OS: macOS 26.6.2 (build 25G83)
The fault is also present in the development version. The font fetch is
TypeScript that a development build runs from source, and the lines above are
unchanged between the v1.11.4 tag and main at 8c87d79. A development build
of main downloads the same single 9Bt4 file.
Quarto check output
Quarto 1.11.4
[✓] Checking environment information...
Quarto cache location: /Users/charlottewickham/Library/Caches/quarto
[✓] Checking versions of quarto binary dependencies...
Pandoc version 3.10.0: OK
Dart Sass version 1.101.0: OK
Deno version 2.7.14: OK
Typst version 0.15.1: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
Version: 1.11.4
Path: /Applications/quarto/bin
[✓] Checking tools....................OK
TinyTeX: v2026.04
Chrome Headless Shell: 150.0.7871.115
VeraPDF: 1.28.2
[✓] Checking LaTeX....................OK
Using: TinyTex
Path: /Users/charlottewickham/Library/TinyTeX/bin/universal-darwin
Version: 2026
[✓] Checking Chrome Headless....................OK
Using: Chrome Headless Shell installed by Quarto
Path: /Users/charlottewickham/Library/Application Support/quarto/chrome-headless-shell/chrome-headless-shell-mac-arm64/chrome-headless-shell
Version: 150.0.7871.115
[✓] Checking basic markdown render....OK
[✓] Checking R installation...........OK
Version: 4.5.2
Path: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources
LibPaths:
- /Users/charlottewickham/Library/R/arm64/4.5/library
- /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library
knitr: 1.51
rmarkdown: 2.30
[✓] Checking Knitr engine render......OK
[✓] Checking Python 3 installation....OK
Version: 3.12.2
Path: /Users/charlottewickham/.pyenv/versions/3.12.2/bin/python3
Jupyter: 5.9.1
Kernels: python3
[✓] Checking Jupyter engine render....OK
[✓] Checking Julia installation...
An AI assistant helped investigate this, grounded in a local clone of
quarto-cli, as described in Using AI tools to investigate.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/command/render/pandoc.ts at lines 1548-1560 and compare its Google Fonts URL construction with the working v2 URL described for src/core/sass/brand.ts. Reproduce the issue with the provided QMD document, then run quarto typst fonts --variants --font-path .quarto/typst/fonts. Done means the requested normal, italic, bold, and bold-italic faces are available for Typst without changing the HTML path.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100