Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/command/render/output-typst.ts
3584 views
1
/*
2
* output-typst.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { dirname, join, normalize, relative } from "../../deno_ral/path.ts";
8
import { ensureDirSync, safeRemoveSync } from "../../deno_ral/fs.ts";
9
10
import {
11
kFontPaths,
12
kKeepTyp,
13
kOutputExt,
14
kOutputFile,
15
kVariant,
16
} from "../../config/constants.ts";
17
import { Format } from "../../config/types.ts";
18
import { writeFileToStdout } from "../../core/console.ts";
19
import { dirAndStem, expandPath } from "../../core/path.ts";
20
import { kStdOut, replacePandocOutputArg } from "./flags.ts";
21
import { OutputRecipe, RenderOptions } from "./types.ts";
22
import { normalizeOutputPath } from "./output-shared.ts";
23
import {
24
typstCompile,
25
TypstCompileOptions,
26
validateRequiredTypstVersion,
27
} from "../../core/typst.ts";
28
import { asArray } from "../../core/array.ts";
29
import { ProjectContext } from "../../project/types.ts";
30
31
export function useTypstPdfOutputRecipe(
32
format: Format,
33
) {
34
return format.pandoc.to === "typst" &&
35
format.render[kOutputExt] === "pdf";
36
}
37
38
export function typstPdfOutputRecipe(
39
input: string,
40
finalOutput: string,
41
options: RenderOptions,
42
format: Format,
43
project?: ProjectContext,
44
): OutputRecipe {
45
// calculate output and args for pandoc (this is an intermediate file
46
// which we will then compile to a pdf and rename to .typ)
47
const [inputDir, inputStem] = dirAndStem(input);
48
const output = inputStem + ".typ";
49
let args = options.pandocArgs || [];
50
const pandoc = { ...format.pandoc };
51
if (options.flags?.output) {
52
args = replacePandocOutputArg(args, output);
53
} else {
54
pandoc[kOutputFile] = output;
55
}
56
57
// when pandoc is done, we need to run the pdf generator and then copy the
58
// output to the user's requested destination
59
const complete = async () => {
60
// input file is pandoc's output
61
const input = join(inputDir, output);
62
63
// run typst
64
await validateRequiredTypstVersion();
65
const pdfOutput = join(inputDir, inputStem + ".pdf");
66
const typstOptions: TypstCompileOptions = {
67
quiet: options.flags?.quiet,
68
fontPaths: asArray(format.metadata?.[kFontPaths]) as string[],
69
};
70
if (project?.dir) {
71
typstOptions.rootDir = project.dir;
72
}
73
const result = await typstCompile(
74
input,
75
pdfOutput,
76
typstOptions,
77
);
78
if (!result.success) {
79
throw new Error();
80
}
81
82
// keep typ if requested
83
if (!format.render[kKeepTyp]) {
84
safeRemoveSync(input);
85
}
86
87
// copy (or write for stdout) compiled pdf to final output location
88
if (finalOutput) {
89
if (finalOutput === kStdOut) {
90
writeFileToStdout(pdfOutput);
91
safeRemoveSync(pdfOutput);
92
} else {
93
const outputPdf = expandPath(finalOutput);
94
95
if (normalize(pdfOutput) !== normalize(outputPdf)) {
96
// ensure the target directory exists
97
ensureDirSync(dirname(outputPdf));
98
Deno.renameSync(pdfOutput, outputPdf);
99
}
100
}
101
102
// final output needs to either absolute or input dir relative
103
// (however it may be working dir relative when it is passed in)
104
return normalizeOutputPath(input, finalOutput);
105
} else {
106
return normalizeOutputPath(input, pdfOutput);
107
}
108
};
109
110
const pdfOutput = finalOutput
111
? finalOutput === kStdOut
112
? undefined
113
: normalizeOutputPath(input, finalOutput)
114
: normalizeOutputPath(input, join(inputDir, inputStem + ".pdf"));
115
116
// return recipe
117
const recipe: OutputRecipe = {
118
output,
119
keepYaml: false,
120
args,
121
format: { ...format, pandoc },
122
complete,
123
finalOutput: pdfOutput ? relative(inputDir, pdfOutput) : undefined,
124
};
125
126
// if we have some variant declared, resolve it
127
// (use for opt-out citations extension)
128
if (format.render?.[kVariant]) {
129
const to = format.pandoc.to;
130
const variant = format.render[kVariant];
131
132
recipe.format = {
133
...recipe.format,
134
pandoc: {
135
...recipe.format.pandoc,
136
to: `${to}${variant}`,
137
},
138
};
139
}
140
141
return recipe;
142
}
143
144