LaTeX na jekyll blogu bez javascriptu

Existuje nepřeberné množství možností, jak implementovat deriváty TeX\TeXu na Vaši webovou stránku. Vyzkoušel jsem jich mnoho, ale bohužel žádná z nich nesplňovala mých 10 požadavků:

  1. Podporuje LaTeX\LaTeX
  2. Implementovatelné do Jekyllu
  3. Nevyužívá JavaScript na straně klienta
  4. Nenačítá žádné externí zdroje
  5. Využívá pouze OpenSource software
  6. Je možné používat $$ notaci
  7. Rozlišuje Inline a Display režimy
  8. Pokud post neobsahuje žádné matematické výroky, tak stránka zbytečně nenačítá k tomu potřebné zdroje
  9. Nahlásí chyby v latexovém výroku už při renderování
  10. 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. eiπ+1=0e^{i\pi} + 1 = 0 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.

itΨ(r,t)=[22μ2+V(r,t)]Ψ(r,t)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.

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:


Známé chyby


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.