Skip to content

python 自定义 fontforge.pyi 文件激活语法高亮

2024-11-02 · 819字 · 3分钟

这两天学习用 fontforge 制作字体。fontforge 的 python api 很方便,奈何它没有 单独的 python 包,而是 用 python C api 调用其 C 源代码最后编译得到.so文件,而非一般的 python module,因此 pylance 无法使用语法高亮。通过询问 gpt 我得知可以用 python index file .pyi文件供 pylance 索引,它的功能和 C 语言中的头文件 .h 类似,只描述函数签名、参数类型、返回类型等接口形状,很适合用于静态分析。与此前在 Typescript 中接触的 .d.ts 文件有异曲同工之妙。

开始我想有没有一种自动化工具可以直接解析.so库生成.pyi文件,随即找到 mypy,运行以下代码:

bash
# sudo apt install python3-fontforge #这会将 fontforge 安装到 ubuntu 系统的顶级 python 解释器(/bin/python) 环境 (dist-package) 中
pip install mypy
stubgen -m fontforge -o . # 前提是从 apt 上安装了 python3-fontforge 包,而不是打包好的应用程序

生成代码(节选)如下:

py
from _typeshed import Incomplete
from typing import Any

__date__: str
__version__: str
hooks: dict
spiroCorner: int
spiroG2: int
spiroG4: int

class font:
    activeLayer: Incomplete
    ascent: Incomplete
    bitmapSizes: Incomplete
    capHeight: Incomplete
    changed: Incomplete

    @classmethod
    def __init__(cls, *args, **kwargs) -> None: ...
    def addAnchorClass(self, *args, **kwargs): ...
    def addContextualSubtable(self, *args, **kwargs): ...
    def addExtrema(self, *args, **kwargs): ...
    def addInflections(self, *args, **kwargs): ...
    def addKerningClass(self, *args, **kwargs): ...
    def addLookup(self, *args, **kwargs): ...
    def addLookupSubtable(self, *args, **kwargs): ...

class glyph:
    activeLayer: Incomplete
    altuni: Incomplete
    anchorPoints: Incomplete
    anchorPointsWithSel: Incomplete
    background: Incomplete
    changed: Incomplete

    def addAnchorPoint(self, *args, **kwargs): ...
    def addExtrema(self, *args, **kwargs): ...
    def addHint(self, *args, **kwargs): ...
    def addInflections(self, *args, **kwargs): ...
    def addPosSub(self, *args, **kwargs): ...
    def addReference(self, *args, **kwargs): ...
    def appendAccent(self, *args, **kwargs): ...
    def autoHint(self, *args, **kwargs): ...
    def autoInstr(self, *args, **kwargs): ...
    def autoTrace(self, *args, **kwargs): ...
    def balance(self, *args, **kwargs): ...
    def boundingBox(self, *args, **kwargs): ...
    def build(self, *args, **kwargs): ...
    def canonicalContours(self, *args, **kwargs): ...
    def canonicalStart(self, *args, **kwargs): ...
    def changeWeight(self, *args, **kwargs): ...
    def clear(self, *args, **kwargs): ...
    def cluster(self, *args, **kwargs): ...
    def condenseExtend(self, *args, **kwargs): ...
    def correctDirection(self, *args, **kwargs): ...
    def doUndoLayer(self, *args, **kwargs): ...
    def draw(self, *args, **kwargs): ...
    def exclude(self, *args, **kwargs): ...
    def export(self, *args, **kwargs): ...
    def genericGlyphChange(self, *args, **kwargs): ...
    def getPosSub(self, *args, **kwargs): ...
    def glyphPen(self, *args, **kwargs): ...

嗯,模块级属性、方法,类级属性、方法,该有的都有,但是没有我最想要的参数名提示、类型提示、docstring。毕竟,fontforge api 的文档 里写的可比这详细多了。于是我在想,这个文档是不是自动生成的?如果是的话,是不是可以用生成文档的办法生成 .pyi

果然经过一番搜索,fontforge 使用了 sphinx 生成文档。然而,在源码仓库里看不懂 调用 sphinx 的代码。对于正常的 python package,sphinx 有一套方便的流程,但恕我实在搞不定 fontforge 这个大型项目。等哪一天 LLM 可以直接分析整个代码库,我一定要问问 fontforge 的 sphinx 文档是怎么自动生成的。

最后万般无奈,我还是从 fontforge 文档提供的函数签名一点点改写 .pyi 文件,好在我要使用的功能不多,所以工作量不大。只需要把该文件放在工作目录下,vscode 的 pylance 就会自动找到,更保险的做法是配置 settings.json 如下:

json
{
  "python.analysis.extraPaths": [
    "path/to/your/type_stubs"
  ],
  "python.analysis.typeCheckingMode": "strict"
}
返回

人同此心,心同此理;如风沐面,若水润心