Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/smc_pyutil/smc_pyutil/ipynb_to_pdf.py
Views: 285
1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3
4
# This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
5
# License: AGPLv3 s.t. "Commons Clause" – read LICENSE.md for details
6
"""
7
Convert ipynb files to pdf using nbconvert's html generating
8
and headless chromium, instead of using LaTeX. This is much
9
faster and more reliable, but potentially doesn't "look" as good,
10
depending on your tastes. It also has a dependency on chromium.
11
"""
12
13
# ATTN: make sure to keep dependencies of this in sync with projects/project/configuration.ts
14
15
### **
16
# This script is deprecated as of
17
#
18
# https://github.com/sagemathinc/cocalc/pull/5583
19
#
20
# which implements much better and more efficient functionality.
21
# In particular, there is now a project api call jupyter_nbconvert
22
# e.g., used in packages/frontend/course/export/export-assignment.ts
23
# that converts notebooks to other formats. It's much more
24
# efficient than upstream nbconvert for html and pdf, and doesn't waste
25
# time importing code each time it is run.
26
###
27
28
from __future__ import absolute_import, print_function
29
from shutil import which
30
import os, sys, time, glob
31
from subprocess import check_call
32
from itertools import repeat, chain
33
34
35
def sanitize_nbconvert_path(path):
36
# same functionality as in packages/util/sanitize-nbconvert.ts
37
# https://github.com/jupyter/nbconvert/issues/911
38
return glob.escape(path)
39
40
41
def ipynb_to_pdf(path):
42
t = time.time()
43
print("-" * 70)
44
print("Convert %s..." % path)
45
if not path.endswith('.ipynb'):
46
err = "every path must end in '.ipynb' but '%s' does not" % path
47
raise ValueError(err)
48
49
browser = None
50
if which("chromium-browser") is not None:
51
browser = "chromium-browser"
52
elif which("google-chrome") is not None:
53
browser = "google-chrome"
54
else:
55
raise Exception("Neither Chrome nor Chromium installed!")
56
print(f"using {browser} to convert to PDF")
57
58
path = os.path.abspath(path)
59
base = path[:-len('ipynb')]
60
pdf = base + 'pdf'
61
html = base + 'tmp.html'
62
check_call([
63
"jupyter",
64
"nbconvert",
65
sanitize_nbconvert_path(path),
66
"--to",
67
"html",
68
"--template",
69
"classic",
70
"--output=%s" % html,
71
])
72
# --no-sandbox so it works in cocalc-docker (see https://stackoverflow.com/questions/43665276/how-to-run-google-chrome-headless-in-docker); should be OK, given our security model...
73
check_call([
74
browser,
75
"--headless",
76
"--disable-gpu",
77
"--no-sandbox",
78
"--print-to-pdf=%s" % pdf,
79
"--run-all-compositor-stages-before-draw",
80
"--virtual-time-budget=10000",
81
html,
82
])
83
os.unlink(html)
84
print("Converted %s to %s in %s seconds" % (path, pdf, time.time() - t))
85
print("-" * 70)
86
87
88
def main():
89
if len(sys.argv) == 1:
90
print("Usage: cc-ipynb-to-pdf [filename1.ipynb] [filename2.ipynb] ...")
91
print(
92
"Converts filename1.ipynb to filename1.pdf, etc., using nbconvert first"
93
)
94
print(
95
"to convert to HTML, then using headless chromium to convert that to PDF."
96
)
97
print(
98
"This is *vastly* more robust and faster than using nbconvert directly,"
99
)
100
print("since that uses LaTeX.")
101
else:
102
for path in sys.argv[1:]:
103
ipynb_to_pdf(path)
104
105
106
if __name__ == "__main__":
107
main()
108
109