基於texlive定製chemfig化學式轉換Python服務鏡像

chemfig

據別人介紹,在繪製平面分子式,乃至化學反應式、機理圖時,大家使用的基本都是ChemDraw。當然ChemDraw是一款強大的軟件,無論是平面的還是立體的分子結構式都能毫不費力地繪製出來。當然這份強大是要錢的,對於平面的分子式或反應式,不要錢而且還可行的方案大致也就LaTeX語言中的Chemfig宏包。
Chemfig是法國學者開發的宏包,εTeX,pdfLaTeX等TeX編譯器都能正常使用,並且相對來說開發是比較活躍的。

texlive

TeX Live 是 TUG (TeX User Group) 發佈並維護的的 TeX 系統,可以稱得上是TeX的官方系統。對於任何階段的TeX用戶,都可以使用TeX Live, 以保持在跨操作系統、跨用戶的TeX文件一致性。

texlive Docker鏡像及服務化改造

texlive的安裝B站有很多教程,目標是需要提供繪製chemfig化學方程式轉換的服務,而texlive軟件本身並不提供相關的api服務,需要對其進行服務化改造,因為考慮容器化部署,需要將texlive封裝成docker鏡像

話不多說,我們選擇的基礎鏡像是 texlive:2020 ,使用Python對外提供服務,關於texlive相關的介紹可以參考博客:chemfig化學式轉換為pdf

拉取鏡像並運行

docker pull texlive:2020

docker run --name texlive -d texlive:2020

docker ps -a | grep  live
dda1561ae866  texlive:2020   "tail -f /dev/null" 8 seconds ago  Up 2  texlive

docker exec -it dda1561ae866 bash

安裝python,製作texlive-python鏡像

# texlive是基於Alpine Linux,目前主流
cat /etc/issue
Welcome to Alpine Linux 3.12

# 修改apk鏡像源
vi etc/apk/repositories

替換文件內容為阿里源:
//mirrors.aliyun.com/alpine/v3.8/main/
//mirrors.aliyun.com/alpine/v3.8/community/

# 更新軟件庫
apk update
fetch //mirrors.aliyun.com/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch //mirrors.aliyun.com/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
v3.8.5-67-gf94de196ca [//mirrors.aliyun.com/alpine/v3.8/main/]
v3.8.5-66-gccbd6a8ae7 [//mirrors.aliyun.com/alpine/v3.8/community/]
OK: 9578 distinct packages available

# 安裝python3
apk add --no-cache python3 python3-dev py-pip

# 驗證安裝
python3 -V
Python 3.6.9
bash-4.4# pip3 -V
pip 18.1 from /usr/lib/python3.6/site-packages/pip (python 3.6)

保存鏡像

docker commit dda1561ae866 textlive-python

至此,我們的擁有python環境texlive鏡像就已經製作好了

服務化改造

python腳本

from flask import Flask, abort, request, jsonify
import os
import subprocess
import uuid
import base64

app = Flask(__name__)


@app.route('/texlive/translate/', methods=['POST'])
def translate():
    if not request.json or 'chemfig' not in request.json:
        abort(400)
    chem_fig = request.json['chemfig']
    # 由於不好測算化學方程式圖形的大小,這裡支持配置紙張大小,我們這裡默認a5paper
    if 'paper' in request.json:
        paper = request.json['paper']
    else:
        paper = 'a5paper'
    tempFile = open("template.tex")
    lines = tempFile.readlines()
    lines[2] = lines[2].replace("a5paper", paper)
    lines[15] = chem_fig + "\n"
    uuidStr = str(uuid.uuid1())
    new_file_name = uuidStr + '.tex'
    newFile = open(new_file_name, "a+")
    for line in lines:
        newFile.write(line)
    newFile.flush()
    newFile.close()
    try:
        # 這裡使用subprocess比較穩定,os.system經常會出現莫名問題,至少這個pdflatex命令用os.system會報錯
        subprocess.run(["pdflatex", "-interaction=nonstopmode", new_file_name])
        pdf_string = open(uuidStr + '.pdf', "rb").read()
        encoded = base64.b64encode(pdf_string)
        pdf_link = "data:application/pdf;base64,{}".format(encoded)
        pdf_link = pdf_link.replace("b'", "").replace("'", "")
    except:
        remove_file(uuidStr)
    else:
        remove_file(uuidStr)

    return jsonify({"image": pdf_link})


def remove_file(uuid_str):
    os.system('rm ' + uuid_str + '.tex')
    os.system('rm ' + uuid_str + '.log')
    os.system('rm ' + uuid_str + '.pdf')
    os.system('rm ' + uuid_str + '.aux')


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080, debug=False)

Latex模板

template.tex

\documentclass{minimal}
\usepackage{xcolor, mol2chemfig}
\usepackage[a5paper, margin=10px, total={6in, 8in}]{geometry}

\usepackage[helvet]{sfmath}
\setcrambond{2.5pt}{0.4pt}{1.0pt}
\setbondoffset{1pt}
\setdoublesep{2pt}
\setatomsep{%(atomsep)spt}
\renewcommand{\printatom}[1]{\fontsize{8pt}{10pt}\selectfont{\ensuremath{\mathsf{#1}}}}

\setlength{\parindent}{0pt}
\setlength{\fboxsep}{0pt}
\begin{document}

\chemfig{H_3C-[:30]N**6(-(=O)-(**5(-N(-CH_3)--N-))--N(-CH_3)-(=O)-)}

\end{document}

mol2chemfig.sty

requirements.txt

Flask

startup.sh

python3 ./main.py

DockerFile

FROM hub.iflytek.com/ainote/texlive-python:2020
COPY main.py /home
COPY mol2chemfig.sty /home
COPY template.tex /home
COPY requirements.txt /home
COPY startup.sh /home
WORKDIR /home
EXPOSE 8080
RUN pip3 install -r requirements.txt && ls
CMD ["bash", "startup.sh"]

服務鏡像製作

構建 texlive-python-api

# dockerfile構建
docker build -t texlive-python-api .
# 打tag
docker tag texlive-python-api xxx.xxx.com/xxx/texlive-python-api:2020
# 推送遠程鏡像倉庫
docker push xxx.xxx.com/xxx/texlive-python-api:2020

跑一個試試

docker run -p 8080:8080 -d --name texlive-python-api texlive-python-api

請求body

{"paper":"a6paper",
"chemfig":"\\chemfig{CH_3-[:108,,1]N-[:54](-[:180,0.85,,,draw=none]\\mcfcringle{1.03})%\n-[:126]N-[:198]-[:270](-[:342]\\phantom{N})-[:210](=[:270]O)-[:150]N(%\n-[:210,,,2]H_3C)-[:90](=[:150]O)-[:30]N(-[:330])-[:90,,,1]CH_3}\n\n\\bigskip\n\n\\chemfig{CH_3-[:108,,1]N-[:54](-[:180,0.85,,,draw=none]\\mcfcringle{1.03})%\n-[:126]N-[:198]-[:270](-[:342]\\phantom{N})-[:210](=[:270]O)-[:150]N(%\n-[:210,,,2]H_3C)-[:90](=[:150]O)-[:30]N(-[:330])-[:90,,,1]CH_3}"}

postman

拷貝出來放在瀏覽器就可以直接訪問了,這個是a6紙張的渲染效果,可以根據圖的大小動態的傳入紙張規格

再來個大的

{"paper":"a3paper",
"chemfig":"\\chemfig{CH_3-[:108,,1]N-[:54](-[:180,0.85,,,draw=none]\\mcfcringle{1.03})%\n-[:126]N-[:198]-[:270](-[:342]\\phantom{N})-[:210](=[:270]O)-[:150]N(%\n-[:210,,,2]H_3C)-[:90](=[:150]O)-[:30]N(-[:330])-[:90,,,1]CH_3}\n\n\\bigskip\n\n\\chemfig{\nO%5\n=[:270]%3\n(\n-[:210]%2\n(\n<[:150]%4\n-[:90.4,2.094]%115\n-[:18]%109\n-[:330]%110\n-[:30,,,,dlh]%111\n-[:90]%112\n-[:150,,,,dlh]%107\n-[:210]%108\n(\n-[:162]\\mcfabove{N}{H}%113\n-[:234]%114\n-[:306,,,,dbl={73}{73}]%->115\n)\n-[:270,,,,dlh]%->109\n)\n-[:270,,,2]HN%1\n-[:330,,2]%93\n(\n-[:270]%95\n-[:210]%96\n-[:270]%97\n-[:330]%98\n-[:30]%99\n-[:330]%100\n-[:270]%101\n-[:210]%102\n-[:270]%103\n)\n=[:30]O%94\n)\n-[:330]\\mcfbelow{N}{H}%6\n-[:30]%7\n(\n<:[:90]%9\n-[:30]%10\n(\n-[:330,,,1]NH_2%12\n)\n=[:90]O%11\n)\n-[:330]%8\n(\n=[:270]O%15\n)\n-[:30]\\mcfabove{N}{H}%13\n-[:330]%14\n(\n<:[:270]%16\n-[:330]%19\n(\n=[:270]O%21\n)\n-[:30,,,1]OH%20\n)\n-[:30]%17\n(\n=[:90]O%22\n)\n-[:330]\\mcfbelow{N}{H}%18\n-[:30]%23\n<:[:90]%24\n(\n=[:150]O%26\n)\n-[:30,,,1]NH%25\n-[:90,,1]%27\n-[:30]%28\n(\n=[:90]O%29\n)\n-[:330]\\mcfbelow{N}{H}%30\n-[:30]%31\n(\n-[:90]%33\n-[:30]%34\n-[:90]%35\n-[:30,,,1]NH_2%36\n)\n-[:330]%32\n(\n=[:270]O%37\n)\n-[:30]\\mcfabove{N}{H}%38\n-[:330]%39\n(\n<:[:270]%43\n-[:330]%44\n(\n-[:30,,,1]OH%46\n)\n=[:270]O%45\n)\n-[:30]%40\n(\n=[:90]O%42\n)\n-[:330]\\mcfbelow{N}{H}%41\n-[:30]%47\n(\n<:[:90]%49\n)\n-[:330]%48\n(\n=[:30]O%51\n)\n-[:270,,,2]HN%50\n-[:330,,2]%52\n(\n<[:30]%54\n-[:330]%55\n(\n-[:270,,,1]OH%57\n)\n=[:30]O%56\n)\n-[:270]%53\n(\n=[:330]O%58\n)\n-[:210,,,2]HN%59\n-[:270,,2]%60\n-[:210]%61\n(\n=[:270]O%62\n)\n-[:150]\\mcfabove{N}{H}%63\n-[:210]%64\n(\n<:[:270]%104\n-[:330,,,1]OH%105\n)\n-[:180,1.732]%91\n(\n-[:150,,,,dlhs]O%92\n)\n-[:270,,,1]NH%90\n-[:210,,1]%82\n(\n<[:270]%84\n(\n-[:330]%86\n-[:270]%87\n(\n-[:210,,,2]HO%89\n)\n=[:330]O%88\n)\n-[:210]%85\n)\n-[:150]%81\n(\n=[:90]O%83\n)\n-[:210]\\mcfbelow{N}{H}%71\n-[:150]%69\n(\n-[:90]%68\n(\n=[:30]O%70\n)\n-[:150]O%67\n-[:90]%65\n(\n<[:30]%66\n)\n-[:150]%->23\n)\n<[:210]%72\n-[:270]%73\n(\n=[:330]O%75\n)\n-[:210]%74\n-[:270,,,,drh]%80\n(\n-[:330,,,1]NH_2%106\n)\n-[:210]%79\n-[:150,,,,drh]%78\n-[:90]%77\n-[:30,,,,drh]%76\n(\n-[:330]%->74\n)\n}\n"}

PS:LaTeX是一種基於ΤΕΧ的排版系統,不光是化學方程式,其他latex語法都可以通過本鏡像渲染,template.tex是模板文件,對latex排版有要求的可以自行定製

參考鏈接

chemfig化學式轉換為pdf://www.cnblogs.com/xiaoqi/p/chemfig.html
mol2chemfig://chimpsky.uwaterloo.ca/mol2chemfig/index
Chemfig中文手冊://chemfig.man.huzheyang.cn/index.html
用latex繪製有機化學分子式://static.latexstudio.net/wp-content/uploads/2016/09/chemfig.pdf