手動搭建latex公式渲染服務器

latex公式渲染有兩種類型,一種是HTML形式展現公式,另外一種是圖片形式展現公式。若是是HTML形式展現公式,渲染是在前端完成的,通常會比較緩慢。知乎採起的方式是以圖片形式展現公式。codecogs是一個latex公式渲染服務,它根據get請求返回一個svg圖片。例如:前端

  • <a target="_blank" href="http://latex.codecogs.com/svg.latex?f(j)=%5Csum_%7Bi%5Cin%5B0,j)%20and%20sum(a%5Bi+1:j%5D)%20%5Cne%200%7D%20f(i)">codecogs</a>,這個服務的缺點是比較慢。
  • 知乎的公式渲染:<a target='_blank' href="https://www.zhihu.com/equation?tex=\frac{1}{3}">https://www.zhihu.com/equation?tex=\frac{1}{3}</a>

本文介紹ubuntu下搭建相似codecogs的公式渲染服務。python

1、安裝latex

sudo apt-get install latex
sudo apt-get install latex-cjk-chinese

#2、latex命令介紹 latex命令用於把tex文件轉換成pdf文件或者dvi文件。dvi是一種設備無關的可打印文件格式。 輸入dvi按兩次tab能夠找到dvisvgm,此命令將dvi文件轉爲svg。shell

#3、編寫服務程序 使用flask編寫服務,經過命令行的方式調用latex獲取svg。在返回時須要注意兩點:flask

  • 設置好content-type,不然客戶端不知道你返回的是什麼格式的圖片
  • 跨域訪問並不須要設置,由於加載的是靜態資源。跨域訪問只須要在header中設置: "Access-Control-Allow-Origin": "*"

使用latex命令時須要注意:ubuntu

  • documentclass必須是minimal,這樣可以保證生成的文件儘可能小。
  • dvisvgm --no-fonts --no-styles,把dvi轉爲svg時取消導出字體和格式,而只是簡單導出一張圖片,不然客戶端找不到這些字體和格式。
  • 使用latex --interaction=nonstopmode,可以保證即使報錯也不會阻塞
  • 要引入amsmath,不然許多宏會找不到
  • 爲了防止用戶上傳不合法公式形成超時的現象,須要使用subprocess模塊,它是非阻塞的,父進程能夠對子進程的運行時間進行監聽。

TODO:跨域

  • 添加緩存功能:對某個公式的請求可能不少,每次不須要調用latex從新生產,直接使用緩存結果。這個優化可能沒有必要,由於當查詢分佈特別分散時,這個優化費力不討好。
  • 添加統計功能:統計不一樣網站的請求次數,用來查看都有哪些人使用了本服務。其實調用別人服務是一件很危險的事情。調用別人的服務就是信任別人的服務,把別人的服務當作本身的一部分。當別人變得不可信任時,本身也就危險了。好比latex公式服務把返回的svg圖片統一替換成某個不合法的圖片。
import os
import signal
import subprocess

from flask import Flask, request, Response

app = Flask(__name__)

file_id = 0
latex_dir = os.path.join(os.path.expanduser("~"), "latex-server")
if not os.path.exists(latex_dir):
    os.mkdir(latex_dir)


def run_command(s, log_file):
    pro = subprocess.Popen(s, shell=True, preexec_fn=os.setsid)
    try:
        pro.wait(1)  # 最多等待1秒鐘
    except Exception as ex:
        print(ex)
        # pro.terminate()
        os.killpg(os.getpgid(pro.pid), signal.SIGTERM)  # 殺死一個進程組
        raise ex


def gets(formula):
    global file_id
    file_id += 1
    now = file_id
    tex_file, dvi_file, svg_file, log_file = [os.path.join(latex_dir, "{}.{}".format(
        now, file_type)) for file_type in "tex dvi svg log".split()]
    open(tex_file, mode='w').write(r"""
\documentclass{minimal}
\usepackage{amsmath}
\begin{document}
$$%s$$
\end{document}
    """ % formula)
    try:
        run_command("latex  --interaction=nonstopmode --output-directory  {} {}".format(
            latex_dir, tex_file), log_file)
        run_command(
            "dvisvgm --no-fonts --no-styles -c2,2 -o {} {}".format(svg_file, dvi_file), log_file)
        svg = open(svg_file).read()  # 若是不存在,那就直接拋出異常吧
        return svg
    except Exception as ex:
        raise ex
    finally:
        # 清理文件
        for i in "tex dvi log aux svg".split():
            filename = os.path.join(latex_dir, "{}.{}".format(now, i))
            if os.path.exists(filename):
                os.remove(filename)


@app.route("/render")
def render():
    formula = request.args['formula']
    try:
        resp = gets(formula)
        return Response(response=resp, headers={
            "Content-Type": "image/svg+xml"
        })
    except Exception as ex:
        print(ex)
        return Response(status=500)


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9988, debug=True)

參考資料

https://cloud.tencent.com/developer/article/1015883緩存

相關文章
相關標籤/搜索