Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/format/typst/format-typst.ts
6438 views
1
/*
2
* format-typst.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { join } from "../../deno_ral/path.ts";
8
9
import { RenderServices } from "../../command/render/types.ts";
10
import { ProjectContext } from "../../project/types.ts";
11
import { BookExtension } from "../../project/types/book/book-shared.ts";
12
import {
13
kCiteproc,
14
kColumns,
15
kDefaultImageExtension,
16
kFigFormat,
17
kFigHeight,
18
kFigWidth,
19
kLogo,
20
kNumberSections,
21
kSectionNumbering,
22
kShiftHeadingLevelBy,
23
kVariant,
24
kWrap,
25
} from "../../config/constants.ts";
26
import {
27
Format,
28
FormatExtras,
29
FormatPandoc,
30
Metadata,
31
PandocFlags,
32
} from "../../config/types.ts";
33
import { formatResourcePath } from "../../core/resources.ts";
34
import { createFormat } from "../formats-shared.ts";
35
import { hasLevelOneHeadings as hasL1Headings } from "../../core/lib/markdown-analysis/level-one-headings.ts";
36
import {
37
BrandNamedLogo,
38
LogoLightDarkSpecifier,
39
} from "../../resources/types/schema-types.ts";
40
import { fillLogoPaths, resolveLogo } from "../../core/brand/brand.ts";
41
import { LogoLightDarkSpecifierPathOptional } from "../../resources/types/zod/schema-types.ts";
42
43
const typstBookExtension: BookExtension = {
44
selfContainedOutput: true,
45
// multiFile defaults to false (single-file book)
46
};
47
48
export function typstFormat(): Format {
49
return createFormat("Typst", "pdf", {
50
execute: {
51
[kFigWidth]: 5.5,
52
[kFigHeight]: 3.5,
53
[kFigFormat]: "svg",
54
},
55
pandoc: {
56
standalone: true,
57
[kDefaultImageExtension]: "svg",
58
[kWrap]: "none",
59
[kCiteproc]: false,
60
},
61
extensions: {
62
book: typstBookExtension,
63
},
64
resolveFormat: typstResolveFormat,
65
formatExtras: async (
66
_input: string,
67
markdown: string,
68
flags: PandocFlags,
69
format: Format,
70
_libDir: string,
71
_services: RenderServices,
72
_offset?: string,
73
_project?: ProjectContext,
74
): Promise<FormatExtras> => {
75
const pandoc: FormatPandoc = {};
76
const metadata: Metadata = {};
77
78
// provide default section numbering if required
79
if (
80
(flags?.[kNumberSections] === true ||
81
format.pandoc[kNumberSections] === true)
82
) {
83
// number-sections imples section-numbering
84
if (!format.metadata?.[kSectionNumbering]) {
85
metadata[kSectionNumbering] = "1.1.a";
86
}
87
}
88
89
// unless otherwise specified, pdfs with only level 2 or greater headings get their
90
// heading level shifted by -1.
91
const hasLevelOneHeadings = await hasL1Headings(markdown);
92
if (
93
!hasLevelOneHeadings &&
94
flags?.[kShiftHeadingLevelBy] === undefined &&
95
format.pandoc?.[kShiftHeadingLevelBy] === undefined
96
) {
97
pandoc[kShiftHeadingLevelBy] = -1;
98
}
99
100
const brand = format.render.brand;
101
const logoSpec = format
102
.metadata[kLogo] as LogoLightDarkSpecifierPathOptional;
103
const sizeOrder: BrandNamedLogo[] = [
104
"small",
105
"medium",
106
"large",
107
];
108
// temporary: if document logo has object or light/dark objects
109
// without path, do our own findLogo to add the path
110
// typst is the exception not needing path but we'll probably deprecate this
111
const logo = fillLogoPaths(brand, logoSpec, sizeOrder);
112
format.metadata[kLogo] = resolveLogo(brand, logo, sizeOrder);
113
// force columns to wrap and move any 'columns' setting to metadata
114
const columns = format.pandoc[kColumns];
115
if (columns) {
116
pandoc[kColumns] = undefined;
117
metadata[kColumns] = columns;
118
}
119
120
// Provide a template and partials
121
// For Typst books, a book extension overrides these partials
122
const templateDir = formatResourcePath("typst", join("pandoc", "quarto"));
123
124
const templateContext = {
125
template: join(templateDir, "template.typ"),
126
partials: [
127
"numbering.typ",
128
"definitions.typ",
129
"typst-template.typ",
130
"page.typ",
131
"typst-show.typ",
132
"notes.typ",
133
"biblio.typ",
134
].map((partial) => join(templateDir, partial)),
135
};
136
137
return {
138
pandoc,
139
metadata,
140
templateContext,
141
};
142
},
143
});
144
}
145
146
function typstResolveFormat(format: Format) {
147
// Pandoc citeproc with typst output requires adjustment
148
// https://github.com/jgm/pandoc/commit/e89a3edf24a025d5bb0fe8c4c7a8e6e0208fa846
149
if (
150
format.pandoc?.[kCiteproc] === true &&
151
!format.pandoc.to?.includes("-citations") &&
152
!format.render[kVariant]?.includes("-citations")
153
) {
154
// citeproc: false is the default, so user setting it to true means they want to use
155
// Pandoc's citeproc which requires `citations` extensions to be disabled (e.g typst-citations)
156
// This adds the variants for them if not set already
157
format.render[kVariant] = [format.render?.[kVariant], "-citations"].join(
158
"",
159
);
160
}
161
}
162
163