LaTeX na jekyll blogu bez javascriptu
[Edit]Existuje nepřeberné množství možností, jak implementovat deriváty u na Vaši webovou stránku. Vyzkoušel jsem jich mnoho, ale bohužel žádná z nich nesplňovala mých 10 požadavků:
- Podporuje
- Implementovatelné do Jekyllu
- Nevyužívá JavaScript na straně klienta
- Nenačítá žádné externí zdroje
- Využívá pouze OpenSource software
- Je možné používat
$$
notaci - Rozlišuje Inline a Display režimy
- Pokud post neobsahuje žádné matematické výroky, tak stránka zbytečně nenačítá k tomu potřebné zdroje
- Nahlásí chyby v latexovém výroku už při renderování
- Možnost nastavit a používat globální macra
Řešení
Napsat vlastní Jekyll plug-in, který pomocí KaTeXu, vyrenderuje LaTeXové výrazy na matematické výroky. Využitím Jekyllovských hook eventů mohu upravit vyrenderované HTML před jeho uložením. Ve vyrenderovaném HTML pomocí regexů najdu LaTeXové výroky a nahradím je vyrederovanými.
Tutorial
1) Reference
Stáhněte si nejnovější verzi KaTeXu. Jediné soubory co budete potřebovat jsou katex.min.css
a katex.min.js
. katex.min.js
ale stačí jen lokálně, a nemusí být na serveru vůbec nahraný). Proto doporučuji přidat do _config.yml
exclude pravidlo:
exclude: ["katex.min.js"]
Každý post obsahující LaTeXové výroky, musí ve svém headru obsahovat následující kód:
{% if page.latex or site.latex %}
<link rel="stylesheet" href="/assets/katex/katex.min.css">
{% endif %}
Pokud chcete LaTeX použít ve vašem postu, stačí nastavit v front matter postu:
---
latex: true
---
nebo globálně v _config.yml
. Globální nastevení ale nedoporučuji pokud Vaše stránka neobsahuje matematické výrazy v každém postu, protože by zbytečně načítala k tomu potřebné zdroje.
2) Instalace execjs gemu
Lokální instalace
Pokud je Váše Jekyllová stránka sestavená jako Ruby Gem, stačí Vám přidat závislost gem 'execjs'
do Vašeho Gemfile
. Pak už jen stačí spustit bundle update
v kořenové složce Vaší Jekyllové stránky.
Globální instalace
Ta je poněkud jednodušší. Stačí nainstalovat gem execjs
následujícím příkazem:
gem install execjs
3) Jekyll plug-in
Ve složce _plugins/
vytvořte .rb
soubor do ve kterém napíšeme náš plugin. Můj se jmenuje katex.rb
. Samotný kód pluginu je:
require 'execjs'
PATH_TO_JS = "./assets/katex/katex.min.js"
katex_modul = ExecJS.compile(open(PATH_TO_JS).read)
count = 0
global_macros = Hash.new
Jekyll::Hooks.register :site, :after_init do |site|
if site.config["latex-macros"] != nil
for macro_definition in site.config["latex-macros"]
global_macros[macro_definition[0]] = macro_definition[1]
end
end
print(" LaTeX: " + global_macros.size.to_s + " macros loaded\n")
end
Jekyll::Hooks.register :documents, :post_render do |doc, payload|
if doc.data["latex"]
rendered_content = doc.output
rendered_content = rendered_content.gsub("% <![CDATA[\n", "")
rendered_content = rendered_content.gsub("%]]>", "")
display_latex = rendered_content.scan(Regexp.new(/<script type="math\/tex; mode=display">(.*?)<\/script>/m)).flatten
count += display_latex.size
for dl in display_latex
rendered_content = rendered_content.gsub('<script type="math/tex; mode=display">' + dl + '</script>',
'<div class="equation" style="font-size: 130%">' +
katex_modul.call("katex.renderToString", dl, {displayMode: true, macros: global_macros}) +
'</div>')
end
inline_latex = rendered_content.scan(Regexp.new(/<script type="math\/tex">(.*?)<\/script>/m)).flatten
count += inline_latex.size
for il in inline_latex
rendered_content = rendered_content.gsub('<script type="math/tex">' + il + '</script>',
'<span class="inline-equation">' +
katex_modul.call("katex.renderToString", il, {displayMode: false, macros: global_macros}) +
'</span>')
end
doc.output = rendered_content
end
end
Jekyll::Hooks.register :site, :after_reset do
count = 0
end
Jekyll::Hooks.register :site, :post_write do
print(" LaTeX: " + count.to_s + " expressions rendered\n")
end
Jediné co musíte udělat je nastavit proměnou PATH_TO_JS
na cestu k souboru katex.min.js
.
Příklady použití
Oba režimy mají stejnou notaci. O tom který se použije rozhoduje Kramdown. Latexový výraz, který bude na samostatném řádku se vyrenderuje v Display režimu a všechny ostatní v inline režimu.
Pokud Kramdown narazí na samostatný breakline znak uprostřed textu, smaže ho a celý text slije do jednoho odstavce. Proto pokud chcete opravdu zalomit řádek, musíte použít dva breakline znaky.
Inline režim
Lorem ipsum dolor sit amet, consectetur adipiscing elit. $$e^{i\pi} + 1 = 0$$ Suspendisse et molestie quam.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse et molestie quam.
Display režim
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
$$
i\hbar\frac{\partial}{\partial t} \Psi(\mathbf{r},t) = \left [ \frac{-\hbar^2}{2\mu}\nabla^2 + V(\mathbf{r},t)\right ] \Psi(\mathbf{r},t)
$$
Suspendisse et molestie quam.
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Suspendisse et molestie quam.
Macra
Pro nastavení globálních macer, stačí v _config.yml
vytvořit list definic:
latex-macros:
- ["\\RR", "\\mathbb{R}"]
- ["\\NN", "\\mathbb{N}"]
- ["\\ZZ", "\\mathbb{Z}"]
V tomto příkladě jsme nastavili:
\RR
na\NN
na\ZZ
na
Známé chyby
- Regex nedetekuje všechny výroky - SOLVED - Kramdown občas přidal do výroku
CDATA
tag, který obsahoval break line znak. Opraveno odstraněnímCDATA
tagu před renderováním. - Načítání macer ignoruje
--config
option - SOLVED - Místo přímého načítání ze souboru (Jekyll.configuration({})["latex-macros"]
) načítám z Jekyllovké cache (site.config["latex-macros"]
). - Latexový výrok musí být na jeden řádek, aby byl regexem detekován - SOLVED - opraveno multiline flagem (
/m
) - Exejs je velmi pomalý (cca 350 výroků za minutu)
- Pokud Latexový výrok zalomíte tak aby jako první znak na řádku bylo
+
nebo-
, Kramdown vytvoří list
Poděkování
Chtěl bych poděkovat mému dobrému kamarádovi Tomáši Slámovi za článek Typesetting math with LaTeX in Jekyll, který byl pro ten můj důležitým zdrojem.