I have:
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: italic and weight: [700, 900] the token italic:700 returns
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.
I have:
Bug description
For
format: typst, Quarto downloads the Google fonts named in the brandinto
.quarto/typst/fonts/. When a font entry gives both astyleand aweight, the two never combine. Quarto downloads the weights at normal styleand 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
:For
style: [normal, italic]andweight: [400, 700]this gives: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 aseparate variant. The parts here are
normal,italic:400and700, so itreturns normal 400, italic 400 and normal 700.
Two faults follow from this:
style: italicandweight: [700, 900]the tokenitalic:700returnsitalic 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 correctital,wght@pairs. For the samebrand it emits
css2?family=Atkinson+Hyperlegible:ital,wght@0,700;1,700, and that URL doesserve italic 700.
Steps to reproduce
Render this document. The brand is in the document YAML, so no
_brand.ymlfile is necessary. It asks for two weights at two styles.
Then ask Typst which variants it found in the font cache:
Actual behavior
No bold-italic face is in the cache:
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: 700andstyle: italic, one face arrives, and it isStyle: Italic, Weight: 400.Two separate
fonts:entries for the same family give the same result. A_brand.ymlfile gives the same result as the document YAML above: bothproduce an identical
.typfile.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 --variantsalso reportsStyle: 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
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.4tag andmainat 8c87d79. A development buildof
maindownloads the same single9Bt4file.Quarto check output
An AI assistant helped investigate this, grounded in a local clone of
quarto-cli, as described in Using AI tools to investigate.