Playwrightの中華フォントを修正してNotoフォントにする

目次

Docker上でPlaywrightを使ってテストしようとすると、いわゆる中華フォントでレンダリングされてしまうことがある。これをIPAフォントに修正したりNotoフォントに修正したりする方法を記載する。また、どのフォントを使ってるかの確認方法も合わせて紹介する。

症状

Docker上でこのようにPlaywrightを--with-depsでインストールして、Webフォントを使っていないようなページ、例えばMDNを開くと画像のように中華フォントになってしまうことがある。

 1FROM ubuntu:24.04
 2RUN apt-get update && apt-get install -y \
 3    pipx \
 4    && \
 5    apt-get clean && \
 6    rm -rf /var/lib/apt/lists/*
 7ENV PATH=/root/.local/bin:${PATH}
 8RUN pipx install uv playwright && \
 9    playwright install --with-deps && \
10    apt-get clean && \
11    rm -rf /var/lib/apt/lists/*
12RUN mkdir /test
13WORKDIR /test
14COPY . /test/
15# pytest-playwright をPythonプロジェクトの依存関係に含む
16RUN uv venv && uv pip install . && uv run playwright install
17CMD ["uv", "run", "pytest"]

WenQuanYi Zen Heiの表示例

この画像では例えばの糸へんやの字が日本語フォントっぽくないことが確認できると思う。これはPlaywrightの依存関係の定義fonts-wqy-zenheiが含まれていて、これが日本語フォントより優先されてしまうためこのような表示になってしまう。

使用されているフォントを確認する

Playwrightで実際にレンダリングで使われているフォント情報を取得するの記事とStackOverflowを参考にして、iframe内の#containerで使われているフォントを取得すると以下のようになっていた。

 1import json
 2
 3from playwright.sync_api import Page
 4
 5def print_font(page: Page):
 6    # このiframeは遅延読み込みなので表示待ちをする
 7    container = page.frame_locator("#frame_monitoring_screen_resolution_or_zoom_level_changes").locator("#container")
 8    container.wait_for()
 9    # CDPセッションを作成
10    client = page.context.new_cdp_session(page)
11    # iframeのdocumentのnodeIdを取得
12    root = client.send("DOM.getDocument", {"depth": 0})
13    iframe_id = client.send("DOM.querySelector", {"nodeId": root["root"]["nodeId"], "selector": ".sample-code-frame"})
14    iframe_desc = client.send("DOM.describeNode", {"nodeId": iframe_id["nodeId"]})
15    iframe_res = client.send(
16        "DOM.resolveNode", {"backendNodeId": iframe_desc["node"]["contentDocument"]["backendNodeId"]}
17    )
18    iframe = client.send("DOM.requestNode", {"objectId": iframe_res["object"]["objectId"]})
19    # #containerのnodeIdを取得
20    container_id = client.send("DOM.querySelector", {"nodeId": iframe["nodeId"], "selector": "#container"})
21    # フォント情報を取得
22    client.send("CSS.enable")
23    fonts = client.send("CSS.getPlatformFontsForNode", {"nodeId": container_id["nodeId"]})
24    print(json.dumps(fonts, ensure_ascii=False, indent=2))
 1{
 2  "fonts": [
 3    {
 4      "familyName": "WenQuanYi Zen Hei",
 5      "postScriptName": "WenQuanYiZenHei",
 6      "isCustomFont": false,
 7      "glyphCount": 95
 8    },
 9    {
10      "familyName": "Liberation Sans",
11      "postScriptName": "LiberationSans",
12      "isCustomFont": false,
13      "glyphCount": 5
14    }
15  ]
16}

IPAフォントにする

IPAフォントにするには、単にfonts-wqy-zenheiをアンインストールすればよい。の糸へんやの字が日本語の漢字になっていることが確認できる。Fontconfigの優先度調整を頑張ればアンインストールせずに済むかもしれないが、大抵アンインストールで対応してる印象。

 1FROM ubuntu:24.04
 2RUN apt-get update && apt-get install -y \
 3    pipx \
 4    && \
 5    apt-get clean && \
 6    rm -rf /var/lib/apt/lists/*
 7ENV PATH=/root/.local/bin:${PATH}
 8RUN pipx install uv playwright && \
 9    playwright install --with-deps && \
10    # fonts-wqy-zenheiを削除する
11    apt-get purge -y fonts-wqy-zenhei && \
12    apt-get clean && \
13    rm -rf /var/lib/apt/lists/*
14RUN mkdir /test
15WORKDIR /test
16COPY . /test/
17# pytest-playwright をPythonプロジェクトの依存関係に含む
18RUN uv venv && uv pip install . && uv run playwright install
19CMD ["uv", "run", "pytest"]

IPAフォントの表示例

 1{
 2  "fonts": [
 3    {
 4      "familyName": "IPAGothic",
 5      "postScriptName": "IPAGothic",
 6      "isCustomFont": false,
 7      "glyphCount": 47
 8    },
 9    {
10      "familyName": "IPAPGothic",
11      "postScriptName": "IPAPGothic",
12      "isCustomFont": false,
13      "glyphCount": 48
14    },
15    {
16      "familyName": "Liberation Sans",
17      "postScriptName": "LiberationSans",
18      "isCustomFont": false,
19      "glyphCount": 6
20    }
21  ]
22}

Notoフォントにする

NotoフォントよりもIPAフォントの方が優先度が高いらしく、次の例のように単純にfonts-notoをインストールしてもIPAフォントのままになってしまう。

 1FROM ubuntu:24.04
 2RUN apt-get update && apt-get install -y \
 3    # fonts-notoを追加する
 4    fonts-noto \
 5    pipx \
 6    && \
 7    apt-get clean && \
 8    rm -rf /var/lib/apt/lists/*
 9ENV PATH=/root/.local/bin:${PATH}
10RUN pipx install uv playwright && \
11    playwright install --with-deps && \
12    # fonts-wqy-zenheiを削除する
13    apt-get purge -y fonts-wqy-zenhei && \
14    apt-get clean && \
15    rm -rf /var/lib/apt/lists/*
16RUN mkdir /test
17WORKDIR /test
18COPY . /test/
19# pytest-playwright をPythonプロジェクトの依存関係に含む
20RUN uv venv && uv pip install . && uv run playwright install
21CMD ["uv", "run", "pytest"]

確実にNotoフォントを適用したい場合は、Notoフォント以外のフォントをすべてアンインストールすればよい。Dockerなのでそれぐらい思い切ったことができるし、Notoフォントで消費している容量の削減にもなる。APTで範囲的にアンインストールするには、apt-patternsにあるように?name()と正規表現を使う。先読みアサーションを使えば1個の正規表現で済むが、試したところ正規表現に(を使えないようなので?and()?not()を組み合わせた。

 1FROM ubuntu:24.04
 2RUN apt-get update && apt-get install -y \
 3    # fonts-notoを追加する
 4    fonts-noto \
 5    pipx \
 6    && \
 7    apt-get clean && \
 8    rm -rf /var/lib/apt/lists/*
 9ENV PATH=/root/.local/bin:${PATH}
10RUN pipx install uv playwright && \
11    playwright install --with-deps && \
12    # fonts-noto*以外のfonts-*を削除する
13    apt-get purge -y '?and(?name(^fonts-),?not(?name(^fonts-noto)))' && \
14    apt-get clean && \
15    rm -rf /var/lib/apt/lists/*
16RUN mkdir /test
17WORKDIR /test
18COPY . /test/
19# pytest-playwright をPythonプロジェクトの依存関係に含む
20RUN uv venv && uv pip install . && uv run playwright install
21CMD ["uv", "run", "pytest"]

すると以下のようにNoto Sans CJK JPで表示できるようになる。

Notoフォントの表示例

 1{
 2  "fonts": [
 3    {
 4      "familyName": "Noto Sans CJK JP",
 5      "postScriptName": "NotoSansCJKjp-Regular",
 6      "isCustomFont": false,
 7      "glyphCount": 95
 8    },
 9    {
10      "familyName": "Noto Sans",
11      "postScriptName": "NotoSans-Regular",
12      "isCustomFont": false,
13      "glyphCount": 6
14    }
15  ]
16}
デレステ: LIVE Carnival 202408 Rank SS突破編成メモ