Skip to content

Commit be404d2

Browse files
Copilotyihui
andauthored
Add Typst (.Rtyp) input format support (#2429)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: yihui <163582+yihui@users.noreply.github.com> Co-authored-by: Yihui Xie <xie@yihui.name>
1 parent a21f6a3 commit be404d2

15 files changed

Lines changed: 128 additions & 13 deletions

.github/workflows/knitr-examples.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,8 @@ jobs:
9292
shell: bash
9393

9494
- uses: actions/upload-artifact@v4
95-
if: failure()
9695
with:
9796
name: knitr-examples
9897
path:
99-
knitr-examples/[0-9][0-9][0-9]-*.*
98+
knitr-examples/*
10099
retention-days: 7

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ knitr-spin.html
1212
/inst/examples/*.tex
1313
/inst/examples/*.sty
1414
.*.Rnb.cached
15+
*.Rcheck/
16+
*.tar.gz

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ Collate:
181181
'hooks-md.R'
182182
'hooks-rst.R'
183183
'hooks-textile.R'
184+
'hooks-typst.R'
184185
'hooks.R'
185186
'otel.R'
186187
'output.R'

NAMESPACE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export(hook_plot_md)
4949
export(hook_plot_rst)
5050
export(hook_plot_tex)
5151
export(hook_plot_textile)
52+
export(hook_plot_typst)
5253
export(hook_pngquant)
5354
export(hook_purl)
5455
export(hook_r2swf)
@@ -62,6 +63,7 @@ export(hooks_markdown)
6263
export(hooks_rst)
6364
export(hooks_sweave)
6465
export(hooks_textile)
66+
export(hooks_typst)
6567
export(image_uri)
6668
export(imgur_upload)
6769
export(include_app)
@@ -115,6 +117,7 @@ export(pat_rnw)
115117
export(pat_rst)
116118
export(pat_tex)
117119
export(pat_textile)
120+
export(pat_typst)
118121
export(plot_crop)
119122
export(purl)
120123
export(rand_seed)
@@ -133,6 +136,7 @@ export(render_markdown)
133136
export(render_rst)
134137
export(render_sweave)
135138
export(render_textile)
139+
export(render_typst)
136140
export(restore_raw_output)
137141
export(rnw2pdf)
138142
export(rocco)

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGES IN knitr VERSION 1.52
22

3+
## NEW FEATURES
4+
5+
- Added support for a new input format `.Rtyp` for the [Typst](https://typst.app) typesetting system (thanks, @blset #2401, @aksigkvgithub #2283). You can use code chunks and inline R expressions in `.Rtyp` files, knit them via `knitr::knit()` to `.typ` output, or compile to PDF directly via `knit2pdf('input.Rtyp')`. See https://github.com/yihui/knitr-examples/blob/master/128-minimal.Rtyp for a minimal example.
6+
37
## BUG FIXES
48

59
- The `alt` attribute of figure images now has HTML tags stripped (via `xfun::strip_html()`) and is properly escaped for use in HTML attributes (via `xfun::html_escape(attr = TRUE)`). Previously, HTML in `fig.cap` (e.g., a link) could appear verbatim in `alt`, and double quotes in captions could break the `alt` attribute value (thanks, @cderv, #2004).

R/hooks-typst.R

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#' @rdname hook_plot
2+
#' @export
3+
hook_plot_typst = function(x, options) {
4+
if (options$fig.show == 'animate') return(hook_plot_html(x, options))
5+
6+
cap = .img.cap(options)
7+
8+
w = options[['out.width']]
9+
h = options[['out.height']]
10+
s = options[['out.extra']]
11+
12+
# build named arguments for #image()
13+
args = c(
14+
if (!is.null(w)) sprintf('width: %s', w),
15+
if (!is.null(h)) sprintf('height: %s', h),
16+
s
17+
)
18+
img_path = paste0(opts_knit$get('base.url'), .upload.url(x))
19+
img_args = paste(c(sprintf('"%s"', img_path), args), collapse = ', ')
20+
21+
if (nzchar(cap)) {
22+
sprintf('\n#figure(\n image(%s),\n caption: [%s],\n)\n', img_args, cap)
23+
} else {
24+
sprintf('\n#image(%s)\n', img_args)
25+
}
26+
}
27+
28+
#' @rdname output_hooks
29+
#' @export
30+
render_typst = function() {
31+
opts_chunk$set(dev = 'pdf')
32+
opts_knit$set(out.format = 'typst')
33+
knit_hooks$set(hooks_typst())
34+
}
35+
36+
#' @rdname output_hooks
37+
#' @export
38+
hooks_typst = function() {
39+
hook.source = function(x, options) {
40+
x = one_string(c(hilight_source(x, 'markdown', options), ''))
41+
lang = tolower(options$lang %n% eng2lang(options$engine))
42+
sprintf('\n```%s\n%s```\n', lang, x)
43+
}
44+
hook.text = function(x, options) {
45+
sprintf('\n```\n%s```\n', x)
46+
}
47+
list(
48+
source = hook.source,
49+
output = function(x, options) {
50+
if (output_asis(x, options)) return(x)
51+
hook.text(x, options)
52+
},
53+
warning = hook.text,
54+
message = hook.text,
55+
error = hook.text,
56+
plot = hook_plot_typst,
57+
inline = function(x) .inline.hook(format_sci(x, 'typst'))
58+
)
59+
}

R/output.R

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ knit = function(
221221
remind_sweave(if (in.file) input, sweave_lines)
222222
opts_knit$set(out.format = switch(
223223
pattern, rnw = 'latex', tex = 'latex', html = 'html', md = 'markdown',
224-
rst = 'rst', brew = 'brew', asciidoc = 'asciidoc', textile = 'textile'
224+
rst = 'rst', brew = 'brew', asciidoc = 'asciidoc', textile = 'textile',
225+
typst = 'typst'
225226
))
226227
}
227228

@@ -370,7 +371,7 @@ auto_out_name = function(input, ext = tolower(file_ext(input))) {
370371
base = sans_ext(input)
371372
name = if (opts_knit$get('tangle')) c(base, '.R') else
372373
if (ext %in% c('rnw', 'snw')) c(base, '.tex') else
373-
if (ext %in% c('rmd', 'rmarkdown', 'qmd', 'rhtml', 'rhtm', 'rtex', 'stex', 'rrst', 'rtextile'))
374+
if (ext %in% c('rmd', 'rmarkdown', 'qmd', 'rhtml', 'rhtm', 'rtex', 'stex', 'rrst', 'rtextile', 'rtyp'))
374375
c(base, '.', substring(ext, 2L)) else
375376
if (grepl('_knit_', input)) sub('_knit_', '', input) else
376377
if (ext != 'txt') c(base, '.txt') else c(base, '-out.', ext)
@@ -382,7 +383,8 @@ ext2fmt = c(
382383
rnw = 'latex', snw = 'latex', tex = 'latex', rtex = 'latex', stex = 'latex',
383384
htm = 'html', html = 'html', rhtml = 'html', rhtm = 'html',
384385
md = 'markdown', markdown = 'markdown', rmd = 'markdown', rmarkdown = 'markdown',
385-
qmd = 'markdown', brew = 'brew', rst = 'rst', rrst = 'rst'
386+
qmd = 'markdown', brew = 'brew', rst = 'rst', rrst = 'rst',
387+
rtyp = 'typst'
386388
)
387389

388390
auto_format = function(ext) {

R/pattern.R

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ all_patterns = list(
5050
chunk.end = '^###[.]\\s+end[.]rcode\\s*$',
5151
ref.chunk = '^\\s*<<(.+)>>\\s*$',
5252
inline.code = '@r +([^@]+)\\s*@',
53-
inline.comment = '^###[.].*')
53+
inline.comment = '^###[.].*'),
54+
55+
`typst` = list(
56+
chunk.begin = '^[\t >]*```+\\s*\\{([a-zA-Z0-9_]+( *[ ,].*)?)\\}\\s*$',
57+
chunk.end = '^[\t >]*```+\\s*$',
58+
ref.chunk = '^\\s*<<(.+)>>\\s*$',
59+
inline.code = '(?<!(^``))(?<!(\n``))`r[ #]([^`]+)\\s*`')
5460
)
5561

5662
.sep.label = '^(#|--)+\\s*(@knitr|----+)(.*?)-*\\s*$' # pattern for code chunks in an R script
@@ -109,7 +115,7 @@ set_pattern = function(type) {
109115
#' @rdname pat_fun
110116
#' @return The patterns object \code{\link{knit_patterns}} is modified as a side
111117
#' effect.
112-
#' @export pat_rnw pat_brew pat_tex pat_html pat_md pat_rst pat_asciidoc pat_textile
118+
#' @export pat_rnw pat_brew pat_tex pat_html pat_md pat_rst pat_asciidoc pat_textile pat_typst
113119
#' @examples # see how knit_patterns is modified
114120
#' knit_patterns$get(); pat_rnw(); knit_patterns$get()
115121
#'
@@ -129,6 +135,8 @@ pat_rst = function() set_pattern('rst')
129135
pat_asciidoc = function() set_pattern('asciidoc')
130136
#' @rdname pat_fun
131137
pat_textile = function() set_pattern('textile')
138+
#' @rdname pat_fun
139+
pat_typst = function() set_pattern('typst')
132140

133141

134142
# helper functions
@@ -148,6 +156,7 @@ detect_pattern = function(text, ext) {
148156
if (ext %in% c('rmd', 'rmarkdown', 'markdown', 'md', 'qmd')) return('md')
149157
if (ext %in% c('rst', 'rrst')) return('rst')
150158
if (ext %in% c('asciidoc', 'rasciidoc', 'adoc', 'radoc')) return('asciidoc')
159+
if (ext %in% c('rtyp')) return('typst')
151160
}
152161
for (p in names(all_patterns)) {
153162
for (i in c('chunk.begin', 'inline.code')) {

R/utils-conversion.R

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ knit2pandoc = function(
5252
#' @param compiler A character string giving the LaTeX engine used to compile
5353
#' the tex document to PDF. For an Rrst file, setting \code{compiler} to
5454
#' \code{'rst2pdf'} will use \code{\link{rst2pdf}} to compile the rst file to
55-
#' PDF using the ReportLab open-source library.
55+
#' PDF using the ReportLab open-source library. For an Rtyp file, setting
56+
#' \code{compiler} to \code{'typst'} will use the \command{typst} command-line
57+
#' tool to compile the typ file to PDF.
5658
#' @param ... Options to be passed to \code{tinytex::\link[tinytex]{latexmk}()}
5759
#' or \code{\link{rst2pdf}()}.
5860
#' @author Ramnath Vaidyanathan, Alex Zvoleff and Yihui Xie
@@ -65,18 +67,31 @@ knit2pandoc = function(
6567
#'
6668
#' #' compile a reST file with rst2pdf
6769
#' ## knit2pdf(..., compiler = 'rst2pdf')
70+
#'
71+
#' #' compile an Rtyp file with typst
72+
#' ## knit2pdf(..., compiler = 'typst')
6873
knit2pdf = function(
6974
input, output = NULL, compiler = NULL, envir = parent.frame(), quiet = FALSE, ...
7075
) {
7176
out = knit(input, output = output, envir = envir, quiet = quiet)
7277
owd = setwd(dirname(out)); on.exit(setwd(owd))
7378
if (is.null(compiler)) {
74-
compiler = if (grepl('\\.rst$', out)) 'rst2pdf' else 'pdflatex'
79+
compiler = if (grepl('\\.rst$', out)) 'rst2pdf' else
80+
if (grepl('\\.typ$', out)) 'typst' else 'pdflatex'
7581
}
7682
if (identical(compiler, 'rst2pdf')) {
7783
if (tolower(file_ext(out)) != 'rst')
7884
stop('for rst2pdf compiler input must be a .rst file')
7985
rst2pdf(basename(out), ...)
86+
} else if (identical(compiler, 'typst')) {
87+
if (tolower(file_ext(out)) != 'typ')
88+
stop('for typst compiler input must be a .typ file')
89+
f = basename(out); o = with_ext(f, 'pdf')
90+
status = system2('typst', c('compile', shQuote(f), shQuote(o)))
91+
if (!file.exists(o)) stop(
92+
'compilation by typst failed (exit status ', status, '); ',
93+
'make sure typst is installed and available in your PATH'
94+
)
8095
} else {
8196
tinytex::latexmk(basename(out), engine = compiler, ...)
8297
}

R/utils.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ format_sci_one = function(
212212
},
213213
html = sci_notation('%s%s10<sup>%s</sup>', b, ' &times; ', lx),
214214
md = sci_notation('%s%s10^%s^', b, '&times; ', lx),
215+
typst = sci_notation('$%s%s10^%s$', b, ' times ', lx),
215216
rst = {
216217
# if AsIs, use the :math: directive
217218
if (inherits(x, 'AsIs')) {

0 commit comments

Comments
 (0)