Upmath是俄罗斯粒子物理学家Dr. Роман Парпалак主导完成的一个开源前端项目——一个基于Markdown+LaTeX的在线编辑器,见它的项目Github主页。它可以实现在Web上显示复杂的数学公式及图形(比如LaTeX Tikz所画的图)的网页内容。
它是将Markdown文本与LaTeX数学表达式转换成HTML页面,并嵌入公式图像(SVG格式)。
项目组件 | 技术栈与职责 |
---|---|
前端 (upmath.me ) | JavaScript + HTML/CSS 前端编辑器,处理 Markdown + LaTeX 转 HTML,并嵌入公式图像。使用 grunt 构建流程。 |
后端渲染服务 (i.upmath.me ) | PHP + TeX Live + nginx + Node.js + Grunt + SVG 工具链:渲染公式为 SVG,并提供 API 服务给前端调用。 |
图像渲染方式 | 使用 TeX Live 和工具链(如 dvisvgm )将 LaTeX 公式渲染为 SVG 矢量图,支持复杂图形(如 TikZ)。(i.upmath.me) |
用户在前端编辑器中输入 Markdown 文本及 LaTeX 公式。
编辑器将 Markdown 转为 HTML。LaTeX 公式部分则使用 的方式,调用后端渲染服务。
后端服务渲染 LaTeX 为 SVG 图片,返回给前端展示。
最终用户在浏览器中看到的是 HTML 页面嵌入的 SVG 数学公式,可复制、分享或发布。
双模块架构:清晰分离编辑与渲染职责,前端专注编辑与呈现,后端专注渲染服务。
技术栈丰富:前端为 JavaScript + Grunt 构建,后端融合 TeX、PHP、SVG 渲染工具,也支持 Docker 部署,便于复现环境。
灵活应用:可嵌入到博客、论坛等任何支持 HTML 的平台,只需贴入一段脚本即可动态渲染数学内容。
在VPS购买的网站购买一个按需要求的VPS,并且获得了公网IPv4:<你的IP地址>,我们这里使用的Debian 12发行版。 需要
在Debian上我们运行
Terminalsudo apt update sudo apt install -y nginx php-fpm nodejs npm yarn git \ texlive-full ghostscript dvisvgm
然后安装Docker:
安装必要工具
Terminalsudo apt install -y ca-certificates curl gnupg lsb-release
添加 Docker 官方 GPG key
Terminalsudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
添加 Docker 官方 apt 源
Terminalecho \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
安装 Docker 引擎
Terminalsudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io
测试
Terminalsudo docker run hello-world
这样我们就准备好了基本的VPS环境,为了实现后面的域名访问,可以在一些域名购买网站上购买一个域名:<你的域名>。
我们之所以选择源码部署,而不直接选择官方Docker部署的原因是因为我们需要在编辑器有使用中文的需求,需要对源代码进行一定改动。
首先,我们从Github下载源码
Terminalgit clone https://github.com/parpalak/upmath.me.git
并进入源码所在的文件夹
Terminalcd i.upmath.me
进入i.upmath.me项目源码文件夹下的tpl文件夹
Terminalcd tpl
tpl文件夹里面有三个文件,可以通过ls查看
Terminalls
我们需要更改document.php这个文件,选择你喜欢的文本编辑器来编辑document.php
PHP<?php
/** @var bool $hasDvisvgmOption */
/** @var string $documentContent */
/** @var \S2\Tex\Tpl\PackageCollection $extraPackages */
/**
* \documentclass[11pt,dvisvgm]{standalone}
* %\usepackage[paperwidth=180in,paperheight=180in]{geometry}
* \usepackage[paperwidth=180in, paperheight=180in,margin=0in]{geometry}
* %\usepackage[a4paper, total={6in, 8in}]{geometry}
* \standaloneconfig{crop=false}
*/
?>
\documentclass[11pt<?php if ($hasDvisvgmOption) { ?>,dvisvgm<?php } ?>]{article}
\usepackage[paperwidth=180in,paperheight=180in]{geometry}
\batchmode
% 注意我们添加了这两行
\usepackage[utf8]{inputenc}
\usepackage{CJKutf8}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{stmaryrd}
\newcommand{\R}{\mathbb{R}}
\newcommand{\lt}{<}
\newcommand{\gt}{>}
% Conditional definitions
\providecommand{\tg}{\operatorname{tg}}
\providecommand{\ctg}{\operatorname{ctg}}
\providecommand{\arctg}{\operatorname{arctg}}
\providecommand{\arcctg}{\operatorname{arcctg}}
\usepackage[verbose]{newunicodechar}
\newunicodechar{¬}{\ensuremath{\neg}}
\newunicodechar{Γ}{\ensuremath{\Gamma}}
\newunicodechar{γ}{\ensuremath{\gamma}}
\newunicodechar{λ}{\ensuremath{\lambda}}
\newunicodechar{φ}{\ensuremath{\varphi}}
\newunicodechar{ψ}{\ensuremath{\psi}}
\newunicodechar{ϕ}{\ensuremath{\varphi}}
\newunicodechar{ᵢ}{\ensuremath{{}_{i}}}
\newunicodechar{₀}{\ensuremath{{}_{0}}}
\newunicodechar{₁}{\ensuremath{{}_{1}}}
\newunicodechar{₂}{\ensuremath{{}_{2}}}
\newunicodechar{₃}{\ensuremath{{}_{3}}}
\newunicodechar{₄}{\ensuremath{{}_{4}}}
\newunicodechar{₅}{\ensuremath{{}_{5}}}
\newunicodechar{₆}{\ensuremath{{}_{6}}}
\newunicodechar{₇}{\ensuremath{{}_{7}}}
\newunicodechar{₈}{\ensuremath{{}_{8}}}
\newunicodechar{₉}{\ensuremath{{}_{9}}}
\newunicodechar{ₙ}{\ensuremath{{}_{n}}}
\newunicodechar{ℓ}{\ensuremath{\ell}}
\newunicodechar{→}{\ensuremath{\rightarrow}}
\newunicodechar{⇒}{\ensuremath{\supset}}
\newunicodechar{⇔}{\ensuremath{\Leftrightarrow}}
\newunicodechar{∅}{\ensuremath{\emptyset}}
\newunicodechar{∈}{\ensuremath{\in}}
\newunicodechar{∘}{\ensuremath{\circ}}
\newunicodechar{∙}{\ensuremath{\bullet}}
\newunicodechar{∧}{\ensuremath{\wedge}}
\newunicodechar{∨}{\ensuremath{\vee}}
\newunicodechar{∼}{\ensuremath{\sim}}
\newunicodechar{≠}{\ensuremath{\neq}}
\newunicodechar{≡}{\ensuremath{\equiv}}
\newunicodechar{⊃}{\ensuremath{\supset}}
\newunicodechar{⊕}{\ensuremath{\oplus}}
\newunicodechar{⊖}{\ensuremath{\ominus}}
\newunicodechar{⊢}{\ensuremath{\vdash}}
\newunicodechar{⊤}{\ensuremath{\top}}
\newunicodechar{⊥}{\ensuremath{\bot}}
\newunicodechar{⊻}{\ensuremath{\veebar}}
\newunicodechar{⟝}{\ensuremath{\vdash}}
\newunicodechar{⬓}{\ensuremath{\square}}
\newunicodechar{Σ}{\ensuremath{\sum}}
\newunicodechar{Π}{\ensuremath{\prod}}
\newunicodechar{ⱼ}{\ensuremath{{}_{j}}}
<?php
echo $extraPackages->getCode();
?>
\pagestyle{empty}
\setlength{\topskip}{0pt}
\setlength{\parindent}{0pt}
\setlength{\abovedisplayskip}{0pt}
\setlength{\belowdisplayskip}{0pt}
\begin{document}
\begin{CJK}{UTF8}{gbsn} % 注意我们添加了这一行
<?php
foreach (['newwrite', 'openout'] as $disabledCommand) {
echo '\\renewcommand{\\' . $disabledCommand . '}{\\errmessage{Command \\noexpand\\' . $disabledCommand . ' is disabled}}', "\n";
}
?>
<?php echo $documentContent; ?>
\end{CJK}% 注意我们添加了这一行
\end{document}
请注意在原来代码中,我们添加了4行,为了尽量减少修改,我们仍然使用pdfLaTeX编译,因此,我们增加了对CJK包的支持
... \usepackage[utf8]{inputenc} \usepackage{CJKutf8} ...
注:如果你需要更多LaTeX的包的支持,就可以在这里的后面添加\usepackage{<你需要的LaTeX包>}
并在\begin{document}和\end{document}之内,添加了CJK的使用,UTF8是文本格式,而gbsn是宋体(你可以按需更改)
... \begin{document} \begin{CJK}{UTF8}{gbsn} % 注意我们添加了这一行 ... \end{CJK}% 注意我们添加了这一行 \end{document}
保存并退出,这样我们就完成了这个包的修改。回到上一级i.upmath.me/
Terminalcd ..
为了保证有中文字体的支持,我们在生成Docker容器前写一个Dockerfile,使用你喜欢的编辑器
Terminalnano Dockerfile
并在其中输入
DockerfileFROM ghcr.io/parpalak/upmath-texlive-docker:2025.0.1 EXPOSE 80 WORKDIR /var/www/i.upmath.me RUN apt-get update && apt-get install -y --no-install-recommends \ fonts-noto-cjk \ latex-cjk-all \ && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get -y --no-install-recommends install \ nginx-extras lua-zlib \ zip unzip \ php8.2-fpm \ php8.2-curl \ php8.2-xml \ php8.2-gd \ composer \ librsvg2-bin \ optipng \ supervisor \ curl gnupg && \ mkdir -p /etc/apt/keyrings && \ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ apt-get update && \ apt-get install -y nodejs && \ apt-get remove -y curl gnupg && \ apt-get autoremove -y && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ rm -rf /var/cache/apt/ COPY . . RUN mkdir -p logs RUN composer install --no-dev RUN npm install -g yarn grunt-cli && \ yarn install && \ grunt && \ yarn install --prod && \ npm uninstall -g yarn grunt-cli RUN mkdir -p /var/run/php-fpm/ RUN cp config.php.dist config.php \ && tlversion=$(cat /usr/local/texlive/20*/release-texlive.txt | head -n 1 | awk '{ print $5 }') \ && sed -i "s/\${tlversion}/${tlversion}/g" config.php RUN cp docker/nginx.conf /etc/nginx/nginx.conf RUN cp docker/www.conf /etc/php/8.2/fpm/pool.d/www.conf && \ cp docker/www-tex.conf /etc/php/8.2/fpm/pool.d/www-tex.conf RUN cp docker/superv.conf /etc/superv.conf ENTRYPOINT [ "/var/www/i.upmath.me/docker/entrypoint.sh" ]
保存并退出即可。
在Terminal中运行
Terminaldocker build -t upmath .
生成Docker容器,并且在8080端口运行Docker容器
docker run -d --name upmath -p 8080:80 upmath
打开购买域名的网站,在<你的域名>中添加两条域名解析
主机记录 | 记录类型 | 线路类型 | 记录值 | TTL |
---|---|---|---|---|
www | A | 默认 | <你的IP地址> | 600 |
@ | A | 默认 | <你的IP地址> | 600 |
编辑/etc/nginx/sites-available/下的default文件
Terminalnano /etc/nginx/sites-available/default
在default最后添加
server { server_name <你的域名> www.<你的域名>; # 所有请求反代到内部服务 <你的IP地址>:8080 location / { proxy_pass http://<你的IP地址>:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/<你的域名>/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/<你的域名>/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = www.<你的域名>) { return 301 https://$host$request_uri; } # managed by Certbot if ($host = <你的域名>) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; server_name <你的域名> www.<你的域名>; return 404; # managed by Certbot }
注意替换所有的<你的IP地址>和<你的域名>为你所使用VPS的IPv4地址和你的域名。
测试Nginx配置
Terminalsudo nginx -t
确保没有报错,那么重启Nginx
Terminalsudo systemctl reload nginx
为了开启HTTPS,我们需要安装Certbot
Terminalsudo apt install certbot python3-certbot-nginx -y
并且申请证书并自动修改Nginx
Terminalsudo certbot --nginx -d <你的域名> -d www.<你的域名>
完成后, Nginx会自动生成443端口的配置,HTTP自动跳转到HTTPS。
这样你就可以通过<你的域名>访问搭建好的Upmath服务了!它还支持中文!大功告成!
当然也欢迎使用我目前搭建好的Upmath服务:https://xumin.net