-
-
Notifications
You must be signed in to change notification settings - Fork 47
Draft: Add mathjax v3 support #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pelson
wants to merge
10
commits into
ipython:master
Choose a base branch
from
pelson:feature/mathjax
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
4a6d813
Add missing characters from dummy-index's adaptation, and adapt the z…
pelson 01b130a
Fine for the surd, not fine for the line (left cap problem).
pelson 74dc38a
Add mathjax poc
pelson 5595082
Proof-of-concept for sqrt symbol
pelson 14554b6
Working sqrt prototype
pelson 7c8fc33
Inline stuff for mathjax
pelson 87288fd
Add correct and programatic vinculum handling
pelson 804fa26
Pretty much perfect for the cases that I've tested (very slight horiz…
pelson 634f352
Include a mathjax integration, and bring it into the normal pipeline …
pelson 128dfe1
Add generated rendered math equations to the samples
pelson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8"> | ||
| <title>xkcd-script + MathJax</title> | ||
|
|
||
| <style> | ||
| body { | ||
| font-family: 'xkcd-script', sans-serif; | ||
| font-size: 1.25em; | ||
| max-width: 920px; | ||
| margin: 2em auto; | ||
| padding: 0 1.5em; | ||
| line-height: 1.7; | ||
| color: #111; | ||
| background: #fff; | ||
| } | ||
|
|
||
| h1 { font-size: 2.2em; margin-bottom: 0.2em; } | ||
| h2 { font-size: 1.5em; margin-top: 2em; border-bottom: 1px solid #ccc; padding-bottom: 0.2em; } | ||
| p { margin: 0.8em 0; } | ||
|
|
||
| /* ── Editor ── */ | ||
| .editor-wrap { | ||
| display: flex; | ||
| gap: 1em; | ||
| align-items: flex-start; | ||
| margin-top: 0.5em; | ||
| } | ||
| #latex-input { | ||
| flex: 1; | ||
| min-height: 220px; | ||
| font-family: monospace; | ||
| font-size: 0.78em; | ||
| padding: 0.6em; | ||
| border: 1px solid #999; | ||
| resize: vertical; | ||
| background: #fffff0; | ||
| box-sizing: border-box; | ||
| } | ||
| #latex-preview { | ||
| flex: 1; | ||
| min-height: 220px; | ||
| padding: 0.6em 1em; | ||
| border: 1px solid #ccc; | ||
| background: #fff; | ||
| box-sizing: border-box; | ||
| overflow-wrap: break-word; | ||
| } | ||
| </style> | ||
|
|
||
| <script> | ||
| // MathJax TeX-input config (delimiters). Must be set BEFORE xkcd-mathjax.js | ||
| // (which then merges in its startup hook) and BEFORE MathJax itself loads. | ||
| MathJax = { | ||
| tex: { | ||
| inlineMath: [['$', '$'], ['\\(', '\\)']], | ||
| displayMath: [['$$', '$$'], ['\\[', '\\]']], | ||
| }, | ||
| }; | ||
| </script> | ||
| <script src="xkcd-script/xkcd-mathjax.js"></script> | ||
| <script id="MathJax-script" async | ||
| src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script> | ||
|
|
||
| </head> | ||
| <body> | ||
|
|
||
| <h1>xkcd-script + MathJax</h1> | ||
| <p>MathJax CHTML with xkcd-script substituted for letter and Greek glyphs.</p> | ||
|
|
||
| <div class="editor-wrap"> | ||
| <textarea id="latex-input" spellcheck="false">Inline vs display large operators: | ||
|
|
||
| $\sum_{k=1}^{n} k$ and $\int_0^1 x^2\,dx$ | ||
|
|
||
| $$\sum_{k=1}^{n} k = \frac{n(n+1)}{2}$$ | ||
|
|
||
| $$\int_0^\infty e^{-x}\,dx = 1$$ | ||
|
|
||
| $$\prod_{p\;\text{prime}} \frac{1}{1-p^{-s}} = \sum_{n=1}^{\infty} \frac{1}{n^s}$$</textarea> | ||
| <div id="latex-preview"></div> | ||
| </div> | ||
|
|
||
| <script> | ||
| const input = document.getElementById('latex-input'); | ||
| const preview = document.getElementById('latex-preview'); | ||
| let renderTimer; | ||
|
|
||
| function renderPreview() { | ||
| MathJax.typesetClear([preview]); | ||
| preview.textContent = input.value; | ||
| MathJax.typesetPromise([preview]).then(() => XkcdMathJax.refresh(preview)); | ||
| } | ||
|
|
||
| input.addEventListener('input', () => { | ||
| clearTimeout(renderTimer); | ||
| renderTimer = setTimeout(renderPreview, 350); | ||
| }); | ||
|
|
||
| // Initial preview render fires once MathJax + xkcd-mathjax finish setup. | ||
| XkcdMathJax.ready.then(renderPreview); | ||
|
|
||
| // On resize the library already re-places overlays on the static formulae; | ||
| // we additionally re-render the live preview so its layout stays in sync. | ||
| let resizeTimer; | ||
| window.addEventListener('resize', () => { | ||
| clearTimeout(resizeTimer); | ||
| resizeTimer = setTimeout(renderPreview, 150); | ||
| }); | ||
| </script> | ||
|
|
||
| <h2>Large operators: inline vs display</h2> | ||
|
|
||
| <p>Inline: $\sum_{k=1}^{n} k$ and $\int_0^1 x^2\,dx$ and $\prod_{k=1}^{n} k$</p> | ||
|
|
||
| <p>Display: | ||
| $$\sum_{k=1}^{n} k = \frac{n(n+1)}{2}$$ | ||
| $$\int_0^\infty e^{-x}\,dx = 1 \qquad \int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}$$ | ||
| $$\prod_{p\;\text{prime}} \frac{1}{1-p^{-s}} = \sum_{n=1}^{\infty} \frac{1}{n^s}$$ | ||
| </p> | ||
|
|
||
| <h2>Algebra</h2> | ||
|
|
||
| <p>Quadratic formula: | ||
| $$x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$</p> | ||
|
|
||
| <p>Pythagorean theorem: $a^2 + b^2 = c^2$</p> | ||
|
|
||
| <p>Binomial theorem: | ||
| $$\sum_{k=0}^{n} \binom{n}{k} x^k y^{n-k} = (x + y)^n$$</p> | ||
|
|
||
| <h2>Calculus</h2> | ||
|
|
||
| <p>Definition of derivative: | ||
| $$f'(x) = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}$$</p> | ||
|
|
||
| <p>Gaussian integral: | ||
| $$\int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}$$</p> | ||
|
|
||
| <p>Product rule: $\dfrac{d}{dx}\bigl[f(x)\,g(x)\bigr] = f'(x)\,g(x) + f(x)\,g'(x)$</p> | ||
|
|
||
| <h2>Greek letters</h2> | ||
|
|
||
| <p>Euler's identity: $e^{i\pi} + 1 = 0$</p> | ||
|
|
||
| <p>$\alpha,\ \beta,\ \gamma,\ \delta,\ \varepsilon,\ \zeta,\ \eta,\ | ||
| \theta,\ \iota,\ \kappa,\ \lambda,\ \mu,\ \nu,\ \xi,\ \pi,\ | ||
| \rho,\ \sigma,\ \tau,\ \upsilon,\ \phi,\ \chi,\ \psi,\ \omega$</p> | ||
|
|
||
| <p>Fourier transform: | ||
| $$\hat{f}(\xi) = \int_{-\infty}^{\infty} f(x)\,e^{-2\pi i x \xi}\,dx$$</p> | ||
|
|
||
| <h2>Physics</h2> | ||
|
|
||
| <p>Einstein: $E = mc^2$</p> | ||
|
|
||
| <p>Schrödinger equation: | ||
| $$i\hbar\frac{\partial\psi}{\partial t} = \hat{H}\psi$$</p> | ||
|
|
||
| <p>Maxwell in free space: | ||
| $$\nabla \cdot \mathbf{E} = 0 \qquad \nabla \times \mathbf{E} = -\frac{\partial \mathbf{B}}{\partial t}$$</p> | ||
|
|
||
|
|
||
| <p>Deeply nested sqrt fractions: | ||
| $$\sqrt{b^2 - 4ac} | ||
| \sqrt{\frac{b^2}{x}}{2a} | ||
| \sqrt{\frac{b^2}{\frac{b^2}{x}}}{2a} | ||
| \sqrt{\frac{b^2}{\frac{b^2}{\frac{b^2}{x}}}}{2a} | ||
| \sqrt{\frac{b^2}{\frac{b^2}{\frac{b^2}{\frac{b^2}{4ac}}}}}{2a}$$ | ||
| </p> | ||
| </body> | ||
| </html> |
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hard coded paths need fixing |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| docker run --rm -v /media/important/github/jupyter/xkcd-font:/work fontbuilder-test python3 /work/xkcd-script/generator/gen_mathjax_font.py |
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't want to ship this. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <title>sqrt line builder</title> | ||
| <style> | ||
| body { font-family: monospace; padding: 20px; background: #f0f0f0; } | ||
| #output svg { display: block; background: white; margin-top: 12px; border: 1px solid #ccc; } | ||
| label { display: block; margin: 10px 0 4px; } | ||
| input[type=range] { width: 600px; vertical-align: middle; } | ||
| #info { margin-top: 8px; color: #555; font-size: 0.9em; } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <h2>sqrt line builder</h2> | ||
|
|
||
| <label><input type="checkbox" id="surdCheck" checked> Include surd (√)</label> | ||
| <label> | ||
| Target width: | ||
| <input type="range" id="widthSlider" min="128" max="2000" value="300"> | ||
| <span id="widthLabel">300</span> pt | ||
| </label> | ||
| <div id="info"></div> | ||
| <div id="output"></div> | ||
|
|
||
| <script> | ||
| // All coordinates are in the source SVG's pt space (viewBox "0 0 170 61"). | ||
| // Cut points derived from pixel-profile analysis of sqrt.png (400x143px → 0.425pt/px). | ||
| const SRC_W = 170, SRC_H = 61; | ||
| const CUT_A = 57.5; // end of surd cap / start of tile (flat zone) | ||
| const CUT_B = 59.0; // start of right cap (still in flat zone, fixes the step) | ||
| const TILE_W = CUT_B - CUT_A; // 42.5 pt — the stretchable flat-bar tile | ||
| const RIGHT_CAP_W = SRC_W - CUT_B; // 85 pt | ||
|
|
||
| const PATH_D = `m 410.6448,596.25455 c -52.01622,-1.52077 -90.0699,-7.17037 -96.13414,-14.11479 -5.06429,-6.95363 -20.44144,-47.81367 -32.84636,-91.70123 -12.40492,-43.88756 -36.13187,-122.67228 -52.61034,-174.52264 -15.47851,-51.85958 -36.11344,-120.67237 -45.40792,-152.58808 -9.28526,-30.91575 -20.52437,-56.81329 -23.52424,-56.78565 -3.99983,0.0369 -17.7873,23.16488 -30.52875,51.28348 C 95.267178,231.14504 70.535454,260.37417 54.416347,247.52215 36.278897,232.68864 140.33939,21.72084 165.33833,21.490482 c 12.99945,-0.119786 25.22008,23.768624 49.91935,99.544258 48.37094,148.5606 70.01505,218.36413 102.04573,330.07373 l 28.93867,101.73767 400.02909,1.31406 c 221.01827,0.96351 522.03313,1.18988 670.02683,-0.17384 147.9937,-1.36371 271.0069,-0.49716 273.0345,2.48429 3.0275,2.97223 3.1104,11.97184 -0.8065,21.00832 -4.8707,14.04548 -64.8498,16.59825 -597.8548,18.5096 -326.00457,1.00395 -632.01001,1.82361 -680.0264,0.26598 z`; | ||
|
|
||
| // Caps extend this far into the tile region so their clipped edges | ||
| // land on flat-bar content rather than at a path outline boundary. | ||
| // The tile is drawn behind; caps (opaque black) paint over any tile | ||
| // bleed in their regions — black on black, no visible change. | ||
| const OVERLAP = 2; | ||
|
|
||
| const slider = document.getElementById('widthSlider'); | ||
| const surdCheck = document.getElementById('surdCheck'); | ||
| const widthLabel = document.getElementById('widthLabel'); | ||
| const info = document.getElementById('info'); | ||
| const output = document.getElementById('output'); | ||
|
|
||
| surdCheck.addEventListener('change', () => { | ||
| const minW = surdCheck.checked | ||
| ? Math.ceil(CUT_A + RIGHT_CAP_W) | ||
| : Math.ceil(2 * RIGHT_CAP_W); | ||
| slider.min = minW; | ||
| if (+slider.value < minW) slider.value = minW; | ||
| redraw(); | ||
| }); | ||
| slider.addEventListener('input', redraw); | ||
|
|
||
| function f(n) { return n.toFixed(3); } | ||
|
|
||
| function redraw() { | ||
| const W = +slider.value; | ||
| widthLabel.textContent = W; | ||
| const surd = surdCheck.checked; | ||
| const leftCapW = surd ? CUT_A : RIGHT_CAP_W; | ||
|
|
||
| const M = Math.max(0, W - leftCapW - RIGHT_CAP_W); | ||
| const sx = M > 0 ? M / TILE_W : 1; | ||
| const tileTx = leftCapW - CUT_A * sx; | ||
| const rightTx = W - RIGHT_CAP_W - CUT_B; | ||
|
|
||
| // Below this threshold the scaled tile is too thin to rasterize cleanly — | ||
| // skip it and let the extended left cap bridge the gap instead. | ||
| const TILE_THRESHOLD = 4; | ||
| const useTile = M >= TILE_THRESHOLD; | ||
|
|
||
| // When tiling: tight sub-clip on the tile, caps extend OVERLAP into it. | ||
| // When not tiling: left cap extends all the way across the gap (+ OVERLAP | ||
| // into the right cap), so no tile clip is needed. | ||
| const leftClipW = useTile ? leftCapW + OVERLAP : leftCapW + M + OVERLAP; | ||
|
|
||
| const clipMid = useTile ? `<clipPath id="cm"> | ||
| <rect x="${f(leftCapW)}" y="0" width="${f(M)}" height="${SRC_H}"/> | ||
| </clipPath>` : ''; | ||
|
|
||
| const clipLeft = `<clipPath id="cl"> | ||
| <rect x="0" y="0" width="${f(leftClipW)}" height="${SRC_H}"/> | ||
| </clipPath>`; | ||
|
|
||
| const clipRight = `<clipPath id="cr"> | ||
| <rect x="${f(W - RIGHT_CAP_W - OVERLAP)}" y="0" width="${f(RIGHT_CAP_W + OVERLAP)}" height="${SRC_H}"/> | ||
| </clipPath>`; | ||
|
|
||
| const mid = useTile ? `<g clip-path="url(#cm)"> | ||
| <g transform="translate(${f(tileTx)},0) scale(${f(sx)},1)"><use href="#src"/></g> | ||
| </g>` : ''; | ||
|
|
||
| // Left cap drawn on top of tile — covers the tile's left clip seam. | ||
| const leftCap = surd | ||
| ? `<g clip-path="url(#cl)"><use href="#src"/></g>` | ||
| : `<g clip-path="url(#cl)"> | ||
| <g transform="translate(${f(SRC_W)},0) scale(-1,1)"><use href="#src"/></g> | ||
| </g>`; | ||
|
|
||
| // Right cap drawn on top of tile — covers the tile's right clip seam. | ||
| const right = `<g clip-path="url(#cr)"> | ||
| <g transform="translate(${f(rightTx)},0)"><use href="#src"/></g> | ||
| </g>`; | ||
|
|
||
| // Render order: tile (back) → left cap → right cap (front). | ||
| output.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" | ||
| width="${W}pt" height="${SRC_H}pt" viewBox="0 0 ${W} ${SRC_H}"> | ||
| <defs> | ||
| <g id="src" transform="translate(0,61) scale(0.1,-0.1)"> | ||
| <path d="${PATH_D}" fill="black"/> | ||
| </g> | ||
| ${clipMid} | ||
| ${clipLeft} | ||
| ${clipRight} | ||
| </defs> | ||
| ${mid} | ||
| ${leftCap} | ||
| ${right} | ||
| </svg>`; | ||
|
|
||
| info.textContent = `left: ${f(leftCapW)}pt | tile: ${f(M)}pt (${f(sx)}×) | right: ${f(RIGHT_CAP_W)}pt | total: ${W}pt`; | ||
| } | ||
|
|
||
| redraw(); | ||
| </script> | ||
| </body> | ||
| </html> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt this works well on github - we should get this rendering nicely.