CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/project/print_to_pdf.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2023 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
//##############################################
7
// Printing an individual file to pdf
8
//##############################################
9
10
import { unlink, writeFile } from "node:fs/promises";
11
import { path as temp_path } from "temp";
12
13
import { execute_code } from "@cocalc/backend/misc_node";
14
import { CoCalcSocket } from "@cocalc/backend/tcp/enable-messaging-protocol";
15
import * as message from "@cocalc/util/message";
16
import { defaults, filename_extension, required } from "@cocalc/util/misc";
17
18
import { getLogger } from "@cocalc/backend/logger";
19
const winston = getLogger("print-to-pdf");
20
21
interface SagewsPrintOpts {
22
path: string;
23
outfile: string;
24
title: string;
25
author: string;
26
date: string;
27
contents: string;
28
subdir: string;
29
base_url?: string;
30
extra_data?: string;
31
timeout?: number;
32
}
33
34
async function print_sagews(opts: SagewsPrintOpts) {
35
opts = defaults(opts, {
36
path: required,
37
outfile: required,
38
title: required,
39
author: required,
40
date: required,
41
contents: required,
42
subdir: required, // 'true' or 'false', if true, then workdir is a generated subdirectory which will retain the temporary tex files
43
base_url: undefined, // the base_url for downloading blobs/images
44
extra_data: undefined, // extra data that is useful for displaying certain things in the worksheet.
45
timeout: 90,
46
});
47
48
let extra_data_file: string | undefined = undefined;
49
let args = [
50
opts.path,
51
"--outfile",
52
opts.outfile,
53
"--title",
54
opts.title,
55
"--author",
56
opts.author,
57
"--date",
58
opts.date,
59
"--subdir",
60
opts.subdir,
61
"--contents",
62
opts.contents,
63
];
64
if (opts.base_url) {
65
args = args.concat(["--base_url", opts.base_url]);
66
}
67
68
let err: Error | undefined = undefined;
69
70
try {
71
if (opts.extra_data != null) {
72
extra_data_file = temp_path() + ".json";
73
args.push("--extra_data_file");
74
args.push(extra_data_file);
75
// NOTE: extra_data is a string that is *already* in JSON format.
76
await writeFile(extra_data_file, opts.extra_data);
77
}
78
79
// run the converter script
80
await new Promise<void>((resolve, reject) => {
81
execute_code({
82
command: "smc-sagews2pdf",
83
args,
84
err_on_exit: true,
85
bash: false,
86
timeout: opts.timeout,
87
cb: (err) => {
88
if (err) {
89
winston.debug(`Issue running smc-sagews2pdf: ${err}`);
90
reject(err);
91
} else {
92
resolve();
93
}
94
},
95
});
96
});
97
} catch (err) {
98
err = err;
99
}
100
101
if (extra_data_file != null) {
102
unlink(extra_data_file); // no need to wait for completion before calling opts.cb
103
}
104
105
if (err) {
106
throw err;
107
}
108
}
109
110
export async function print_to_pdf(socket: CoCalcSocket, mesg) {
111
let pdf;
112
const ext = filename_extension(mesg.path);
113
if (ext) {
114
pdf = `${mesg.path.slice(0, mesg.path.length - ext.length)}pdf`;
115
} else {
116
pdf = mesg.path + ".pdf";
117
}
118
119
try {
120
switch (ext) {
121
case "sagews":
122
await print_sagews({
123
path: mesg.path,
124
outfile: pdf,
125
title: mesg.options.title,
126
author: mesg.options.author,
127
date: mesg.options.date,
128
contents: mesg.options.contents,
129
subdir: mesg.options.subdir,
130
extra_data: mesg.options.extra_data,
131
timeout: mesg.options.timeout,
132
});
133
break;
134
135
default:
136
throw new Error(`unable to print file of type '${ext}'`);
137
}
138
139
// all good
140
return socket.write_mesg(
141
"json",
142
message.printed_to_pdf({ id: mesg.id, path: pdf })
143
);
144
} catch (err) {
145
return socket.write_mesg(
146
"json",
147
message.error({ id: mesg.id, error: err })
148
);
149
}
150
}
151
152