Live at ipynbtopdf.xyz.
Converts Jupyter .ipynb notebooks to PDF entirely in the browser. No backend, no upload,
no LaTeX toolchain — the notebook is parsed and rendered client-side, then handed to the
browser's own print engine so the PDF keeps real, selectable text.
npm install
npm run devPushing to master deploys. After a deploy that adds or changes pages, npm run indexnow sends every sitemap URL to IndexNow (Bing and friends) so they recrawl right away; the key file it needs lives in public/.
src/lib/ipynb.ts is the whole converter:
- Parse — accepts nbformat 4 and upgrades the legacy nbformat 3 layout (cells under
worksheets,inputinstead ofsource,pyoutinstead ofexecute_result, MIME data as barepng/latex/htmlkeys). - Render — Markdown via
markdown-it, code viahighlight.js, maths via KaTeX, and outputs by MIME preference:text/html→text/latex→ SVG → raster → plain text. LaTeX is tried first so a figure that ships both TeX and a bitmap becomes crisp vector maths, falling back to the bitmap when KaTeX cannot typeset it. - Sanitize — DOMPurify, with
<use>re-allowed (matplotlib draws every marker with it) but its references confined to same-document fragments.
- Every inlined SVG gets its ids prefixed. Two exports of one figure carry identical
element ids; once both are on the page the second figure's
url(#…)resolves to the first one's definitions and it renders blank. - A lone
\rin stream output collapses the line,\r\ndoes not. The first is a progress bar redrawing itself; the second is a Windows line ending. - Print margins live in
@page, not as padding. Padding applies once across the whole flow, which leaves content flush against the paper edge at every page break. @emnapi/coreand@emnapi/runtimeare devDependencies nothing imports. They are dependencies of@img/sharp-wasm32, which npm lists in the lockfile but whose own dependencies it will not resolve while running on Windows. A Linux CI then runsnpm ci, finds them missing, and refuses to install. Declaring them puts them in the lockfile so the build host is satisfied. Drop them once the lockfile is generated on Linux.
There is no unit suite. The converter is checked against real notebooks — the corpus at https://github.com/odewahn/ipynb-examples is a good one, and every file in it exercises the nbformat 3 path.