找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 10|回复: 0

[教程] 通过文本标签插入图片Latex

[复制链接]
发表于 昨天 20:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本贴使用cunsom_text_tag注册标签tex,实现在文本中插入公式,公式来自于latex语句。
可以查看附件图片来查看效果。
下文介绍代码。关于该代码的应用(即,为什么我要做这个功能),在代码之后。


版本:RenPy 8.5.2
代码:方便起见,建议单开一个latex.rpy。思路:通过python的subprocess包,调用其他程序,生成latex图片放入文件夹,再被游戏调用。
被调用的程序,以及更多详细内容,在下文注释里有,请参考。

init python:
    import os
    import subprocess
    import hashlib

    # 以下是相关文件地址,请读者根据自己的情况调整
    # TinyTeX 根目录(相对 game 目录)
    LATEX_ROOT = os.path.join(renpy.config.gamedir, "TinyTeX")
    # 公式缓存目录
    CACHE_DIR = os.path.join(renpy.config.gamedir, "cache", "latex_formulas")
    # 公式渲染默认参数


    LATEX_BIN = os.path.join(LATEX_ROOT, "bin", "windows", "latex.exe")
    # 生成png的路径
    # DVIPNG_BIN = os.path.join(LATEX_ROOT, "bin", "windows", "dvipng.exe")
    # 生成svg的路径。
    DVISVGM_BIN = os.path.join(LATEX_ROOT, "bin", "windows", "dvisvgm.exe")
    # 我最终转向了svg,只是保留了png的代码。下面的被注释代码也是同理

    # 分别是默认字号和作为对比的标准字号

    # 我们通过一个比例来缩放图片,所以这里的字号并不直接对应字体大小
    default_fontsize = 7
    usual_fontsize = 10


    # 根据公式的latex代码用hash生成固定长度的合法文件名
    def latex_formula_path(code):
        hash_key = hashlib.md5(f"{code}".encode("utf-8")).hexdigest()
        # output_pic = os.path.join(CACHE_DIR, f"formula_{hash_key}.png")
        output_pic = os.path.join(CACHE_DIR, f"formula_{hash_key}.svg")
        print(code)
        return hash_key, output_pic


    # 根据源码生成图片文件
    def latex_formula_pic(code):
        hash_key, output_pic = latex_formula_path(code)
        if os.path.exists(output_pic):
            return output_pic

        # LaTeX 源码模板(无需用户加 $ 符号)
        # 正式版会注释掉这段代码,并移除掉约300M的TyniTex-1 on Windows in 2026,彼时on_testing被设置为False
        # https://github.com/rstudio/tinytex-releases
        # 如果缓存图片丢失,可能需要用户下载这个,然后解除下面的注释(将gallery_files.rpy中的on_testing设置为True)
        # 在TinyTeX\bin\windows下使用cmd命令
        # tlmgr install amsmath amssymb graphics
        # 来安装基础包
        if on_testing:
            tex_template = r"""
            \documentclass{{article}}
            \usepackage{{amsmath,amssymb,color}}
            \pagestyle{{empty}}
            \everymath{{\displaystyle}}
            \begin{{document}}
            ${code}$
            \end{{document}}
            """.format(code=code)

            temp_tex = os.path.join(CACHE_DIR, f"temp_{hash_key}.tex")
            temp_dvi = os.path.join(CACHE_DIR, f"temp_{hash_key}.dvi")

            with open(temp_tex, "w", encoding="utf-8") as f:
                f.write(tex_template)

            try:
                # 编译为 DVI
                subprocess.run(
                    [LATEX_BIN, "-interaction=nonstopmode", "-output-directory", CACHE_DIR, temp_tex],
                    check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
                )

                # 透明PNG
                # subprocess.run(
                #     [
                #         DVIPNG_BIN,
                #         "-D", "300",
                #         "-bg", "Transparent",
                #         "-T", "tight",
                #         "-o", output_pic, temp_dvi
                #     ], check=True, capture_output=True
                # )

                # 透明SVG
                subprocess.run(
                    [
                        DVISVGM_BIN,
                        "-b", "min",    # 最小边框
                        "-Z", "3.0",    # 缩放
                        "-o", output_pic,
                        "--no-fonts=1",
                        temp_dvi
                    ], check=True, capture_output=True
                )

            except subprocess.CalledProcessError:
                return None
            finally:
                # 清理临时文件
                for ext in [
                    ".tex",
                    ".dvi",
                    ".aux",
                    ".log"
                ]:
                    f = temp_tex.replace(".tex", ext)
                    if os.path.exists(f):
                        os.remove(f)
        return output_pic


    # custom_text_tag函数定义
    def tex_tag(tag, argument, contents):
        if len(contents)==0:
            return ""
        img_path = latex_formula_pic(contents[0[1])
        # 渲染公式        if not img_path:
            print("Not img_path")
            return [(renpy.TEXT_TEXT, "渲染失败"
        # 计算图片缩放比例
        # svg作为存储笔画而非像素点的图片文件,不仅体积通常更小,且更扛缩放
        if argument:
            scale = float(argument) / usual_fontsize
        else:
            scale = default_fontsize / usual_fontsize
        # 缩放图片并竖直平移
        # RenPy的文本标签返回的可视化组件,默认是左上角对齐文本,这导致图片更大时,文字与图片看着不协调
        path = img_path.replace("\\", "/")
        img = Transform(
            path,
            # 缩放尺寸
            zoom=scale,
            # 竖直平移
            # 具体的计算,和文字的大小,以及窗口的尺寸,可能都有关。读者请根据自己的情况调整。
            yoffset = (gui.text_size - im.Image(path).load().get_height()*scale)/2 + 5
        )
        return [(renpy.TEXT_DISPLAYABLE, img
    # 注册文本标签
    config.custom_text_tags["tex" = tex_tag




关于该代码的目的——如果我们需要在文本中灵活的插入数学公式,甚至公式数目很多时,开发者希望能够通过简单的文本标签命令,写入latex语句来返回公式图片。

比起一个一个公式图片制作,使用该方法可以更灵活的写代码以及改代码。
除了latex,也有markdown、typist等等其他的能够自由输入公式的语言。本教程提供了一个利用subprocess的处理方法,读者有兴趣可以用其他的程序。例如其实我朋友试过用mathjax来生成,而不是TyniTex来生成我的latex语句。思路是类似的。
另外,如上所述,写代码的过程中,可能会出现写错的公式等等,在调试过程中可能会出现额外的,废弃的图片。
读者可以根据上述代码写一个脚本,在开发阶段的末期,使用脚本删掉所有废弃的图片,以及生成一些前面改动过相信可以成功处理,但又懒得回前面去运行一边的图片。

文本标签可以使用在对话框里,也可以出现在可视组件里

文本标签可以使用在对话框里,也可以出现在可视组件里
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号 )

GMT+8, 2026-6-6 01:39 , Processed in 0.027703 second(s), 7 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表