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

typst: generic font families / font stack names #11918

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ _quarto:
typst:
ensureTypstFileRegexMatches:
-
- '#set text\(font: \("Georgia", "serif"\)\); #table\('
- '#set text\(font: \("Georgia", "Linux Libertine"\)\); #table\('
- []
---

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
format:
html:
quality: 1
pdf:
quality: na
typst:
quality: 2
comment: "table only"
include-in-header:
text: |
#set text(fallback: false)
dashboard:
quality: 1
docx:
quality: na
pptx:
quality: na
keep-typ: true
_quarto:
tests:
typst:
ensureTypstFileRegexMatches:
-
- '#set text\(font: \("Linux Libertine"\)\); #table\('
- '#show heading: set text\(font: "Roboto", \)'
- '#show raw.where\(block: true\): set text\(font: "DejaVu Sans Mono", \)'
- []
ensurePdfRegexMatches:
-
- 'heading is roboto'
- 'linux libertine'
- 'code should appear in a monospace font'
- []
brand:
typography:
base: serif
headings: sans-serif
monospace: monospace
---
# heading is `#context text.font`{=typst}

base is `#context text.font`{=typst}

```{=html}
<table style="font-family: serif;">
<tr><td>A</td><td>B</td></tr>
</table>
```

```
// This code should appear in a monospace font
```

4 changes: 4 additions & 0 deletions src/command/render/pandoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Document } from "../../core/deno-dom.ts";
import { execProcess } from "../../core/process.ts";
import { dirAndStem, normalizePath } from "../../core/path.ts";
import { mergeConfigs } from "../../core/config.ts";
import { quartoConfig } from "../../core/quarto.ts";

import {
Format,
Expand Down Expand Up @@ -1513,6 +1514,9 @@ async function resolveExtras(
}
fontdirs.add(font_cache);
}
const srcDir = Deno.env.get("QUARTO_SRC_PATH") ||
join(quartoConfig.sharePath(), "../../src");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use Deno.env.get("QUARTO_SRC_PATH") || join(quartoConfig.sharePath(), "../../src") in a few different places in our codebase. I wonder if we should be adding a srcPath() to quartoConfig

fontdirs.add(join(srcDir,'resources/fonts'));
let fontPaths = format.metadata[kFontPaths] as Array<string> || [];
if (typeof fontPaths === "string") {
fontPaths = [fontPaths];
Expand Down
38 changes: 37 additions & 1 deletion src/resources/filters/modules/typst_css.lua
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ local function output_length(length, warnings)
if not csf then
output_warning(warnings, 'unit ' .. length.unit .. ' is not supported in ' .. length.csslen )
return nil
end
end
return csf(length.value, length.unit, length.csslen, warnings)
end

Expand Down Expand Up @@ -593,6 +593,10 @@ local function quote(s)
return '"' .. s .. '"'
end

local function dequote(s)
return s:gsub('^["\']', ''):gsub('["\']$', '')
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is safe here, but it's always terrifying to consider the full implications of quoting, escaping, etc. There's no chance that fonts can have , in their titles, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this is also about L662 below)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shudder:

The spec does not seem to ban commas in font family names. In practice this seems to be something that happens in PostScript because of weights and styles of a family, but CSS handles this differently (and better).


local same_weights = {
'thin',
'light',
Expand Down Expand Up @@ -634,6 +638,36 @@ local function translate_font_weight(w, warnings)
end
end

local generic_font_families = {
['sans-serif'] = 'Roboto',
serif = 'Linux Libertine', -- would be Libertinus Serif in Typst 0.12 but we also need to choose our own
math = 'New Computer Modern Math',
monospace = 'DejaVu Sans Mono',
}

local gff_synonyms = {
['ui-sans-serif'] = 'sans-serif',
['system-ui'] = 'sans-serif',
['ui-serif'] = 'serif',
['ui-monospace'] = 'monospace'
}

local function translate_font_family(ff)
ff = gff_synonyms[ff] or ff
return generic_font_families[ff] or ff
end

local function translate_font_family_list(sl)
local strings = {}
for s in sl:gmatch('([^,]+)') do
s = dequote(trim(s))
s = translate_font_family(s)
table.insert(strings, quote(s))
end
return '(' .. table.concat(strings, ', ') ..')'
end


local function translate_border_style(v, _warnings)
local dash
if v == 'none' then
Expand Down Expand Up @@ -760,6 +794,8 @@ return {
translate_border_style = translate_border_style,
translate_border_color = translate_border_color,
translate_font_weight = translate_font_weight,
translate_font_family = translate_font_family,
translate_font_family_list = translate_font_family_list,
consume_width = consume_width,
consume_style = consume_style,
consume_color = consume_color
Expand Down
10 changes: 5 additions & 5 deletions src/resources/filters/quarto-post/typst-brand-yaml.lua
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ function render_typst_brand_yaml()
if headings and next(headings) then
quarto.doc.include_text('in-header', table.concat({
'#show heading: set text(',
conditional_entry('font', headings.family),
conditional_entry('font', _quarto.modules.typst.css.translate_font_family(headings.family)),
conditional_entry('weight', _quarto.modules.typst.css.translate_font_weight(headings.weight)),
conditional_entry('style', headings.style),
conditional_entry('fill', headings.color, false),
Expand All @@ -137,7 +137,7 @@ function render_typst_brand_yaml()
if monospaceInline and next(monospaceInline) then
quarto.doc.include_text('in-header', table.concat({
'#show raw.where(block: false): set text(',
conditional_entry('font', monospaceInline.family),
conditional_entry('font', _quarto.modules.typst.css.translate_font_family(monospaceInline.family)),
conditional_entry('weight', _quarto.modules.typst.css.translate_font_weight(monospaceInline.weight)),
conditional_entry('size', monospaceInline.size, false),
conditional_entry('fill', monospaceInline.color, false),
Expand All @@ -156,7 +156,7 @@ function render_typst_brand_yaml()
if monospaceBlock and next(monospaceBlock) then
quarto.doc.include_text('in-header', table.concat({
'#show raw.where(block: true): set text(',
conditional_entry('font', monospaceBlock.family),
conditional_entry('font', _quarto.modules.typst.css.translate_font_family(monospaceBlock.family)),
conditional_entry('weight', _quarto.modules.typst.css.translate_font_weight(monospaceBlock.weight)),
conditional_entry('size', monospaceBlock.size, false),
conditional_entry('fill', monospaceBlock.color, false),
Expand Down Expand Up @@ -307,7 +307,7 @@ function render_typst_brand_yaml()
local base = _quarto.modules.brand.get_typography('base')
if base and next(base) then
meta.brand.typography.base = {
family = base.family,
family = _quarto.modules.typst.css.translate_font_family(base.family),
size = base.size,
}
end
Expand All @@ -322,7 +322,7 @@ function render_typst_brand_yaml()
local weight = _quarto.modules.typst.css.translate_font_weight(headings.weight or base.weight)
weight = weight and pandoc.RawInline('typst', tostring(quote_string(weight)))
meta.brand.typography.headings = {
family = headings.family or base.family,
family = _quarto.modules.typst.css.translate_font_family(headings.family or base.family),
weight = weight,
style = headings.style or base.style,
decoration = headings.decoration or base.decoration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,6 @@ function render_typst_css_property_processing()
end
end

local function dequote(s)
return s:gsub('^["\']', ''):gsub('["\']$', '')
end

local function quote(s)
return '"' .. s .. '"'
end

local function translate_vertical_align(va)
if va == 'top' then
return 'top'
Expand Down Expand Up @@ -270,15 +262,6 @@ function render_typst_css_property_processing()
end
return span
end

local function translate_string_list(sl)
local strings = {}
for s in sl:gmatch('([^,]+)') do
s = s:gsub('^%s+', '')
table.insert(strings, quote(dequote(s)))
end
return '(' .. table.concat(strings, ', ') ..')'
end

return {
Table = function(tab)
Expand All @@ -289,7 +272,7 @@ function render_typst_css_property_processing()
for clause in tabstyle:gmatch('([^;]+)') do
local k, v = to_kv(clause)
if k == 'font-family' then
tab.attributes['typst:text:font'] = translate_string_list(v)
tab.attributes['typst:text:font'] = _quarto.format.typst.css.translate_font_family_list(v)
has_typst_text = true
end
if k == 'font-size' then
Expand Down
Binary file added src/resources/fonts/Roboto-Regular.ttf
Binary file not shown.
25 changes: 25 additions & 0 deletions tests/docs/smoke-all/typst/typst-css/big-numbers.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: "Untitled"
format: typst
keep-typ: true
---

```{=typst}
#text(font: "Roboto")[Roboto]

#text(font: "Linux Libertine")[Linux Libertine]

#text(font: "New Computer Modern Math")[New Computer Modern Math]

#text(font: "DejaVu Sans Mono")[DejaVu Sans Mono]
```

```{r}
library(gt)
df <- data.frame(a = c("aa111bbb", "abcdef"), b = c(1, 2))
tab <- gt::gt(df)
# reliably recreate fonts failure from https://github.com/quarto-dev/quarto-cli/issues/11683
opt_table_font(tab,
font = c("InvalidFontName", "system-ui", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"),
add = FALSE)
```
Loading