From 0bf473ed2390c9389050211af888420c952af3a8 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 07:06:23 +0900 Subject: [PATCH 1/9] =?UTF-8?q?docs(PLAN63):=20base=20=E3=82=A4=E3=83=A1?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=81=AE=E6=97=A5=E6=9C=AC=E8=AA=9E=E3=81=AE?= =?UTF-8?q?=E6=8F=8F=E7=94=BB=E3=81=A8=E3=80=81=E6=96=87=E6=9B=B8=E3=82=92?= =?UTF-8?q?=E6=89=B1=E3=81=86=E8=BB=BD=E9=87=8F=E3=81=AE=E9=81=93=E5=85=B7?= =?UTF-8?q?=E3=81=AE=E8=A6=81=E6=B1=82=E4=BB=95=E6=A7=98=E3=81=A8=E8=A8=AD?= =?UTF-8?q?=E8=A8=88=20(#161,=20#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit base コンテナで sans-serif が中国語フェイス(WenQuanYi Zen Hei)へ解決される問題と、 Office 文書・PDF を扱う軽量の道具が無い問題を、1 本の設計にまとめた。 どちらも containers/base/Dockerfile の同じ apt / COPY の区画を触るため 1 本にした。 実装は別のブランチで行う。この Pull Request は設計文書だけを載せる。 Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering-design.md | 406 +++++++++++++++++++ issues/PLAN63_base-image-rendering.md | 240 +++++++++++ 2 files changed, 646 insertions(+) create mode 100644 issues/PLAN63_base-image-rendering-design.md create mode 100644 issues/PLAN63_base-image-rendering.md diff --git a/issues/PLAN63_base-image-rendering-design.md b/issues/PLAN63_base-image-rendering-design.md new file mode 100644 index 00000000..47478c2e --- /dev/null +++ b/issues/PLAN63_base-image-rendering-design.md @@ -0,0 +1,406 @@ +# PLAN63: base イメージの日本語の描画と、文書を扱う軽量の道具 の設計 + +要求と受け入れ条件は [PLAN63_base-image-rendering.md](PLAN63_base-image-rendering.md) にある。 +この文書は「どう作るか」だけを扱う。 + +## 機能一覧 + +| # | 機能 | 誰が使うか | +| --- | --- | --- | +| F1 | 総称ファミリ(`sans-serif` / `sans` / `serif` / `monospace`)と、イメージに無い書体名の指定を、Noto CJK の JP フェイスへ向ける | base とその派生イメージで文字を描くすべての道具(Chromium / Playwright / PDF の生成 / 画像の生成) | +| F2 | 中国語・韓国語を明示した指定を、その言語の、同じ様式のフェイスへ向ける | 同上 | +| F3 | PDF を画像にする・調べる、OOXML を壊さずに読み書きする、欧文の字幅を正しく測る道具を base へ入れる | `document-skills` を使う利用者 | +| F4 | 上の 3 つを回帰テストで固定し、利用者向け文書と CHANGELOG を合わせる | devbase の開発者・利用者 | + +## 解決の経路 + +fontconfig は `/etc/fonts/fonts.conf` から始まり、そこにある `` 1 行で `conf.d` を +**ファイル名の番号順**に読む。`local.conf` は `conf.d/51-local.conf` 経由で読まれる。 + +```mermaid +graph TD + FC["/etc/fonts/fonts.conf
include conf.d の 1 行だけ"] --> D[conf.d を番号順に読む] + D --> C30["30-metric-aliases.conf
Arial→Liberation Sans
Calibri→Carlito / Cambria→Caladea"] + C30 --> C50["50-user.conf
→ ~/.config/fontconfig/fonts.conf"] + C50 --> C51["51-local.conf
→ /etc/fonts/local.conf
★ 置き場所はここ"] + C51 --> C64["64-wqy-zenhei.conf
sans-serif の prefer に WenQuanYi Zen Hei"] + C64 --> C65["65-nonlatin.conf
sans-serif の prefer 一覧にも WenQuanYi Zen Hei"] + C65 --> C70["70-fonts-noto-cjk.conf"] +``` + +**`` は、一致した総称ファミリの直前へ挿入する(prepend)。** そのため +**最も早く読まれた `` が先頭に残る。** 効くのは順序が後だからではなく、先だからである。 +`51` は `64` / `65` より先なので、`local.conf` の指定が勝つ。 + +## 実測(2026-09-22 / arm64 の `devbase-base:latest`) + +Ubuntu 26.04 / fontconfig 2.17.1 / イメージのサイズ 7.09GB。稼働中のコンテナへ設定と +パッケージを入れて測った(イメージは建て直していない)。 + +### 解決先の表(この設計が固定するもの) + +| 指定 | 変更前 | 変更後 | +| --- | --- | --- | +| `sans-serif` | WenQuanYi Zen Hei | **Noto Sans CJK JP** | +| `sans-serif:lang=ja` | WenQuanYi Zen Hei | **Noto Sans CJK JP** | +| `sans` | WenQuanYi Zen Hei | **Noto Sans CJK JP** | +| `serif` | WenQuanYi Zen Hei | **Noto Serif CJK JP** | +| `monospace` | WenQuanYi Zen Hei Mono | **Noto Sans Mono CJK JP** | +| `Noto Sans JP` | WenQuanYi Zen Hei | **Noto Sans CJK JP** | +| `Meiryo` / `Yu Gothic` / `MS PGothic` | WenQuanYi Zen Hei | **Noto Sans CJK JP** | +| `Zen Kaku Gothic New`(未導入) | WenQuanYi Zen Hei | **Noto Sans CJK JP** | +| `Arial` | Liberation Sans | Liberation Sans | +| `Times New Roman` | Liberation Serif | Liberation Serif | +| `Courier New` | Liberation Mono | Liberation Mono | +| `Calibri` | WenQuanYi Zen Hei | **Carlito**(パッケージの追加による) | +| `Cambria` | WenQuanYi Zen Hei | **Caladea**(同上) | +| `sans-serif:lang=zh-cn` | Noto Sans CJK SC | Noto Sans CJK SC | +| `serif:lang=zh-cn` | Noto Serif CJK SC | Noto Serif CJK SC | +| `monospace:lang=zh-cn` | Noto Sans Mono CJK SC | Noto Sans Mono CJK SC | +| `sans-serif:lang=ko` | WenQuanYi Zen Hei | **Noto Sans CJK KR** | +| `serif:lang=ko` | (未測定) | **Noto Serif CJK KR** | +| `Arial:lang=zh-cn` | Liberation Sans | Liberation Sans | +| `WenQuanYi Zen Hei`(名指し) | WenQuanYi Zen Hei | WenQuanYi Zen Hei | +| `IPAPGothic`(名指し) | IPAPGothic | IPAPGothic | + +`fc-match -s sans-serif:lang=ja` の並びも変わる。 + +| | 1 番目 | 2 番目 | 3 番目 | +| --- | --- | --- | --- | +| 変更前 | WenQuanYi Zen Hei | IPAPGothic | Loma(タイ語) | +| 変更後 | **Noto Sans CJK JP** | WenQuanYi Zen Hei | IPAPGothic | + +### パッケージの追加の実測 + +`apt-get install --no-install-recommends` で 6 つを指定した結果である。 + +| 項目 | 値 | +| --- | --- | +| 指定するパッケージ | 6 | +| 依存を含めて新規に入るパッケージ | **24** | +| ディスクの増分 | **約 24 MB**(apt のリストとキャッシュを含む測り方。7.09GB に対して +0.34%) | +| 使えるようになるコマンド | `pdftoppm` / `pdfinfo` / `pdffonts` / `pdftocairo` | +| 使えるようになるモジュール | `PIL` 12.1.1 / `defusedxml` 0.7.1 / `lxml` 6.0.2 | +| 入らないもの | `soffice` / `libreoffice` / `pip` / `pip3` | + +## 構成要素 + +| 要素 | 変更 | 責務 | +| --- | --- | --- | +| `containers/base/fonts-local.conf`(新設) | 足す | 総称ファミリの先頭を日本語フェイスにする。未導入の書体の受け皿を置く。中国語・韓国語を明示した指定を守る。**先頭に、置き場所を動かせない理由をコメントで持つ** | +| `containers/base/Dockerfile` の 1 つ目の `RUN` の `apt-get install` | 変える | `fonts-noto-cjk` の行の隣へ 6 パッケージを足す | +| `containers/base/Dockerfile` の末尾の `COPY` 群 | 変える | `COPY --chmod=0644 fonts-local.conf /etc/fonts/local.conf` を足し、直後に `RUN sudo fc-cache -f` を置く | +| `tests/containers/test_base_dockerfile_fonts.py`(新設) | 足す | Docker を起動せずに、Dockerfile と `fonts-local.conf` の**形**を固定する | +| `tests/containers/test_base_image_font_matching.py`(新設) | 足す | 建てたイメージの中で `fc-match` の**解決先の表**を固定する。イメージが無い / 古いときは skip | +| `docs/user/container-operations.md` | 変える | base の説明に、日本語のフェイスと文書の道具の行を足す | +| `CHANGELOG.md` | 変える | `[Unreleased]` に `### Added`(6 パッケージ)と `### Fixed`(日本語が中国語フォントで描画される)。**イメージを建て直すまで反映されない**ことを添える | + +次のものは変えない。 + +- `fonts-wqy-zenhei` の導入(要求の前提 2) +- `npx playwright install --with-deps chromium` とその後のクリーンアップ(前提 5、#220) +- 派生イメージの Dockerfile(すべて `FROM devbase-base:latest`) +- `containers/lfm`(base 由来ではない) + +## 入出力の契約 + +### `containers/base/fonts-local.conf` + +先頭のコメントは**受け入れ条件 8 が要求する内容**(置き場所を動かせない理由と、その実測)を持つ。 + +```xml + + + + + + sans-serifNoto Sans CJK JP + sansNoto Sans CJK JP + serifNoto Serif CJK JP + monospaceNoto Sans Mono CJK JP + + + + Noto Sans CJK JP + + + + + sans-serif + zh-cn + Noto Sans CJK SC + + + +``` + +規則は 4 つの ``、1 つの受け皿、そして **4 つの総称ファミリ × 2 つの言語 = 8 つ**の +`` で構成する。 + +| 総称ファミリ | `lang=zh-cn` で前置する | `lang=ko` で前置する | +| --- | --- | --- | +| `sans-serif` | `Noto Sans CJK SC` | `Noto Sans CJK KR` | +| `sans` | `Noto Sans CJK SC` | `Noto Sans CJK KR` | +| `serif` | `Noto Serif CJK SC` | `Noto Serif CJK KR` | +| `monospace` | `Noto Sans Mono CJK SC` | `Noto Sans Mono CJK KR` | + +これらのフェイスは `fonts-noto-cjk` / `fonts-noto-cjk-extra` に既にあり、追加の導入は要らない +(`fc-list : family` で JP / SC / KR の Sans・Serif・Sans Mono の 9 つの存在を確認済み)。 + +### `containers/base/Dockerfile` の差分の形 + +**1 つ目の `RUN`(最初の `apt-get install`)**: `fonts-noto-cjk fonts-noto-cjk-extra` の行の隣へ +足す。理由は決定 4。 + +```dockerfile + libnss3 libxrandr2 libxss1 \ + fonts-noto-cjk fonts-noto-cjk-extra \ + fonts-crosextra-carlito fonts-crosextra-caladea \ + poppler-utils python3-pil python3-defusedxml python3-lxml; \ +``` + +**末尾の `COPY` 群**(`ai-cli-aliases.sh` / `tmux.conf` と同じ区画、`USER ubuntu` より後): + +```dockerfile +COPY --chmod=0644 fonts-local.conf /etc/fonts/local.conf +RUN sudo fc-cache -f +``` + +`COPY` の所有者は `USER` の指定によらず root になるため、既存の `tmux.conf` と同じ形でよい。 +`fc-cache` は root で走らせる必要があるので `sudo` を付ける(同じ区画の +`sudo install -d` / `sudo ln -sf` と同じ流儀。`ubuntu` は NOPASSWD の sudo を持つ)。 + +## 処理の流れ + +ビルドの層と、この変更が触る位置。 + +```mermaid +graph TD + L1["RUN 1: apt(locales / git / fonts-noto-cjk …)
+ docker / terraform / gh / node / chromium
★ ここへ 6 パッケージを足す"] --> L2["RUN: ユーザーとグループ"] + L2 --> L3["RUN: aws / gcloud / uv / npm globals"] + L3 --> L4["RUN: bao"] + L4 --> L5["RUN(ubuntu): claude / agy / kiro
npx playwright install --with-deps chromium
→ ここで fonts-wqy-zenhei が入る
→ 直後に sudo rm -rf ~/.cache"] + L5 --> L6["COPY 群: ai-cli-aliases.sh / tmux.conf / entrypoint.sh
★ ここへ fonts-local.conf の COPY と fc-cache -f を足す"] +``` + +**`fc-cache -f` は `L5` より後でなければならない。** `L5` の `--with-deps` が +`fonts-wqy-zenhei` / `fonts-ipafont-gothic` / `fonts-liberation` などを入れ、その直後に +`~/.cache` を消す。`L5` より前で走らせると、後から入った書体を知らないキャッシュが残る。 + +## 決定の記録 + +### 決定 1: 置き場所は `/etc/fonts/local.conf` から動かさない + +`conf.d/99-*.conf` へ置くと効かない。同じ内容で実測した(2026-09-22、arm64)。 + +| 置き場所 | `fc-match sans-serif` | `fc-match sans-serif:lang=ja` | +| --- | --- | --- | +| `/etc/fonts/local.conf` | **Noto Sans CJK JP** | **Noto Sans CJK JP** | +| `/etc/fonts/conf.d/99-devbase-fonts.conf` | WenQuanYi Zen Hei | WenQuanYi Zen Hei | + +理由は「解決の経路」に書いたとおりで、`` が prepend であり、**最も早く読まれた +`` が先頭に残る**ためである。`51-local.conf` は `64` / `65` より先に読まれる。 + +**この実測では、もう 1 つ分かったことがある。** `99-` へ置いた場合でも +`` の規則(zh-cn / ko の前置)は効いていた。効かないのは +`` だけである。**規則の種類によって順序への依存が違う**ため、 +「99 でも一部は効く」ことを知らないと、部分的に直ったのを見て置き場所の問題を見落とす。 + +この理由は `containers/base/fonts-local.conf` の先頭のコメントにも同じ内容で残す(受け入れ条件 8)。 + +**`conf.d/00-*.conf` のように小さい番号を使う案は採らない。** 動きはするが、 +`local.conf` は fontconfig が「システムの局所的な設定」のために用意した場所で、番号の +付け替えで順序を取りに行くのは他のパッケージの番号と競合する。 + +### 決定 2: 中国語・韓国語の規則は、総称ファミリを名指ししたときだけ効かせる + +**#161 の本文にある形(`lang` だけを見て前置する)は採らない。実測で壊れるものが見つかった。** + +3 つの変種を同じイメージの中で比べた(2026-09-22、arm64)。 + +| 指定 | 規則なし | #161 の形(`lang` だけ) | この設計(`family` + `lang`) | +| --- | --- | --- | --- | +| `sans-serif:lang=zh-cn` | Noto Sans CJK SC | Noto Sans CJK SC | Noto Sans CJK SC | +| `serif:lang=zh-cn` | Noto Serif CJK SC | **Noto Sans CJK SC** ← 様式が崩れる | Noto Serif CJK SC | +| `monospace:lang=zh-cn` | Noto Sans Mono CJK SC | **Noto Sans CJK SC** ← 等幅でなくなる | Noto Sans Mono CJK SC | +| `sans-serif:lang=ko` | **Noto Sans CJK JP** ← 韓国語が日本語の字形になる | Noto Sans CJK KR | Noto Sans CJK KR | +| `serif:lang=ko` | **Noto Serif CJK JP** ← 同上 | **Noto Sans CJK KR** ← 様式が崩れる | Noto Serif CJK KR | +| `Arial:lang=zh-cn` | Liberation Sans | **Noto Sans CJK SC** ← 欧文が奪われる | Liberation Sans | +| `Times New Roman:lang=zh-cn` | Liberation Serif | **Noto Sans CJK SC** ← 同上 | Liberation Serif | + +**`binding="strong"` の前置は、名指しの書体よりも強い。** そのため `lang` だけを条件にすると、 +`lang=zh-cn` が付いたすべてのパターン ── `Arial` や `Times New Roman` を名指しした欧文の +指定を含む ── から、指定した書体を奪う。Chromium は中国語のページで `lang=zh-cn` を +載せるため、これは絵に描いた話ではない。 + +**規則を 1 つも置かない案も採らない。** `zh-cn` は OS 既定の `65-nonlatin.conf` と +`70-fonts-noto-cjk.conf` が正しく扱うので規則なしでも合うが、**`ko` は合わない** +(決定 4 の `sans-serif` の prefer が JP を先頭にするため、韓国語が日本語の字形になる)。 +`ko` だけ書くと非対称で、なぜ `zh-cn` が無いのかが後から読めない。8 つ並べて対称にする。 + +### 決定 3: 未導入の書体の受け皿は、弱い結合の `append` 1 つで足りる + +`` は、すべてのパターンの末尾へ +`Noto Sans CJK JP` を足す。**弱い結合なので、実在する指定を妨げない。** `Arial` は +`30-metric-aliases.conf`(スロット 30、このファイルより先)の強い結合で Liberation Sans へ +向くため、受け皿は末尾に付くだけで結果を変えない。実測で確認した(`Arial` / `Times New Roman` / +`Courier New` / `Calibri` / `Cambria` のいずれも変わらない)。 + +条件(``)を付けて「未導入のときだけ」足す形は採らない。fontconfig に「その書体が +実在するか」を問う `` は無く、弱い結合がまさにその働きをする。 + +### 決定 4: 6 パッケージは、1 つ目の `RUN` の最初の `apt-get install` の一覧へ足す + +Dockerfile の 1 つ目の `RUN` は `apt-get install` を 2 回呼ぶ。1 回目は Ubuntu の標準の +アーカイブから(`locales` / `git` / `fonts-noto-cjk` など)、2 回目は足したリポジトリから +(`docker-ce` / `terraform` / `gh` / `nodejs` / `chromium-browser`)。 + +**2 つの一覧はどちらも同じ `RUN` の中にあり、キャッシュの鍵は 1 つである。** どちらへ書いても +キャッシュの効き方は同じになる。6 つはすべて標準のアーカイブにあり外部のリポジトリを要さない +ので、1 回目の一覧へ、既にあるフォントの行の隣へ置く。 + +**新しい `RUN` を立てる案は採らない。** 立てれば、この 6 つを後から足し引きしても 1 つ目の +巨大な層のキャッシュは無効にならない。しかし代償が 3 つある。 + +| 代償 | 内容 | +| --- | --- | +| 層が 1 つ増える | base は既に層が多い。派生イメージ 7 つがこの上に積む | +| `apt-get update` をもう 1 回走らせる | 1 つ目の `RUN` は最後に `rm -rf /var/lib/apt/lists/*` でリストを消す。新しい `RUN` はリストを取り直す必要がある | +| クリーンアップを書き写す | `apt-get clean` と `rm -rf` を 2 か所に持つことになる | + +**この 6 つが後から変わる頻度は低い。** 一方 1 つ目の `RUN` は、NodeSource の設定スクリプトを +`curl | bash` で毎回取りに行くなど、外からの変化でどのみち無効になりうる。層を増やして +守るほどの利得が無い。 + +### 決定 5: `COPY` と `fc-cache -f` は末尾の `COPY` 群へ置く + +**`fc-cache -f` は、Playwright が `--with-deps` でフォントを入れる `RUN` より後でなければ +ならない。** その `RUN` は `fonts-wqy-zenhei` / `fonts-ipafont-gothic` / `fonts-liberation` などを +入れ、直後に `~/.cache` を消す。 + +末尾へ置くことには、キャッシュの上の利点もある。`fonts-local.conf` を書き換えたとき、 +無効になるのは末尾の数層だけで、巨大な `RUN` は建て直されない。 + +**フォントのパッケージを入れる 1 つ目の `RUN` の中で `fc-cache` を走らせる案は採らない。** +Playwright が後から入れるフォントを知らないキャッシュが残り、しかも `fonts-local.conf` を +1 文字直すたびにイメージ全体が建て直しになる。 + +なお `fc-cache` は `local.conf` の内容を反映するためではない(設定は照合のたびに読まれる)。 +走らせるのは、`~/.cache` を消した後に `/var/cache/fontconfig` を作り直し、コンテナの初回起動 +時のキャッシュ生成を避けるためである。 + +### 決定 6: 回帰テストは 2 段にする。形は Docker なしで、解決先は Docker ありで固定する + +**Dockerfile の文字列検査だけでは足りない。** 固定したいのは「`COPY` の行があること」では +なく「`fc-match sans-serif` が日本語を返すこと」で、後者は文字列からは分からない。決定 2 の +ような壊れ方(`lang` の条件が広すぎて欧文を奪う)は、Dockerfile を読んでも見えない。 + +| テスト | Docker | 何を固定するか | +| --- | --- | --- | +| `tests/containers/test_base_dockerfile_fonts.py` | 不要 | 6 パッケージが一覧にあること。`COPY` の宛先が `/etc/fonts/local.conf` であり `conf.d/` ではないこと。`fc-cache -f` が `COPY` より後にあること。`fonts-local.conf` が整形式の XML で、4 つの `` と 8 つの `` を持つこと。先頭のコメントが置き場所の理由(`51-local.conf` / `conf.d` / `99`)に触れていること。`libreoffice` / `soffice` / `pip` を入れていないこと。`fonts-wqy-zenhei` を消していないこと | +| `tests/containers/test_base_image_font_matching.py` | 要る | 「解決先の表」の各行。1 回の `docker run` で全部の `fc-match` を採り、行ごとに突き合わせる | + +**Docker が要るテストは、`tests/snapshot/test_restore_incremental.py` の先例に合わせる。** +そこは fixture の中で `shutil.which('docker')` → `docker info` → `docker image inspect` の 3 段を +見て `pytest.skip` する(marker は使わない。`pyproject.toml` に marker の登録も `addopts` も無い)。 + +**それに 1 段足す。イメージの中に `/etc/fonts/local.conf` が無ければ skip する。** 理由は、 +この変更より前に建てた `devbase-base:latest` を持っている人が全員 `pytest tests/` で赤くなる +のを避けるためである。skip の文言に `devbase build base --no-cache` を書き、建て直せば検査が +効くようにする。**古いイメージを「失敗」として知らせる案は採らない。** 正しい作業ツリーで +テストが赤くなり、赤の意味が「壊れている」と「イメージが古い」で混ざる。 + +**このテストは CI では動かない。** `.github/workflows/ci.yml` にイメージを建てるジョブは 1 つも +無く、runner に `devbase-base:latest` は無いので skip になる。さらに +**`release/v3.7.0` を base にした Pull Request では検査ジョブが 1 件も動かない** +(`on.pull_request.branches` が `main` だけ。#216)。**`gh pr checks` の `no checks reported` と +`mergeStateStatus: CLEAN` は「通った」ことを意味しない。** そのため、解決先の表の証跡は +手元で採って Pull Request 本文へ貼る。 + +**環境変数(`DEVBASE_IMAGE_TESTS=1` など)で明示的に有効化する案は採らない。** 既存の +Docker を使うテストが環境変数を要求しておらず、流儀が 2 つに割れる。イメージの中に +`/etc/fonts/local.conf` があるかどうかは、「この検査が意味を持つ状態か」をそのまま表す。 + +### 決定 7: 設定は Dockerfile のヒアドキュメントではなく、独立したファイルにする + +`containers/base/` のビルドコンテキストはこのディレクトリそのものなので、ファイルを置けば +`COPY` で届く(`ai-cli-aliases.sh` / `tmux.conf` と同じ形)。独立したファイルにすると、 +XML として検査でき、差分が読め、置き場所の理由のコメントを長く書ける。#161 が求めている +「ファイル先頭のコメント」も、ヒアドキュメントの中では Dockerfile のコメントと混ざる。 + +### 決定 8: 日本語を既定にし、言語を明示しない中国語は日本語の字形で描く + +base の利用者は日本語話者で、扱う文書も日本語が多い。`lang` を伴わない `sans-serif` は +どちらかの言語を選ばざるをえず、**「日本語の文書が中国語の字形で描かれる」を裏返して +「言語を明示しない中国語の文書が日本語の字形で描かれる」にする。** + +裏返す先が小さいことは実測で確かめてある。中国語を明示した指定(`lang=zh-cn`)と、中国語の +書体を名指しした指定(`WenQuanYi Zen Hei`)はどちらも変わらない。Chromium は +`` のページで `lang` を載せる。 + +### 決定 9: 利用者の上書きの口は塞がない + +`/etc/fonts/conf.d/50-user.conf` が `~/.config/fontconfig/fonts.conf` を読む。スロット 50 は +`51-local.conf` より**先**なので、利用者が自分の `fonts.conf` で別の `` を書けば +そちらが勝つ。実測で確認した(利用者側で `sans-serif` を `WenQuanYi Zen Hei` に戻せた)。 + +この性質はこの設計が作るものではなく、`/etc/fonts/local.conf` を選んだことの帰結である。 +`conf.d/00-*.conf` を使っていたら、利用者の設定より先に読まれて上書きを塞いでいた。 + +## テスト設計 + +| 受け入れ条件 | 何で確かめるか | +| --- | --- | +| 1・2・3・4 | `test_base_image_font_matching.py` の解決先の表(JP の行)。Docker が無い / イメージが古いときは skip | +| 5・11 | 同じ表の欧文の行(`Arial` / `Times New Roman` / `Courier New` / `Calibri` / `Cambria`) | +| 6 | 同じ表の `lang` を明示した行 8 つ。**決定 2 の壊れ方を捕まえるのはこの行である** | +| 7 | `test_base_dockerfile_fonts.py`: `COPY` の宛先が `/etc/fonts/local.conf` であること、`conf.d/` を宛先にする `COPY` が無いこと、`fc-cache -f` が 1 度だけで `COPY` より後にあること | +| 8 | `test_base_dockerfile_fonts.py`: `fonts-local.conf` の先頭のコメントが `51-local.conf` と `conf.d` と `99` に触れていること。設計文書の側は目視 | +| 9・10・12 | `test_base_image_font_matching.py` と同じ `docker run` の中で、`command -v` と `python3 -c "import …"` の結果も採る | +| 13 | ビルドの前後の `docker images`。手で測って Pull Request 本文へ貼る | +| 14 | `uv run --locked pytest tests/ -q` | +| 15 | `devbase build base --no-cache` | +| 16 | 派生イメージを 1 つ建て直して `fc-match sans-serif` を見る。手で確かめて Pull Request 本文へ貼る | + +`test_base_image_font_matching.py` は `docker run` を**セッションで 1 回**に抑える +(`scope="session"` の fixture が 1 つのスクリプトを走らせ、結果を辞書にして返す)。 +`fc-match` を 20 回別々に `docker run` すると、コンテナの起動だけで数十秒かかる。 + +## 未確認のまま残ること + +| 項目 | 内容 | +| --- | --- | +| LibreOffice と Chromium での実際の描画 | どちらも base に無いため実機未検証。確かめたのは `fc-match` の水準まで。LibreOffice は fontconfig とは別の照合も持つため、解決先が `fc-match` と一致しないことがある(#161 の由来になった `volareinc/nyle-dx` PR #5 では `NotoSansCJKsc` が埋め込まれていた)。`containers/docs`(#219)を作るときに、そこで確かめる | +| amd64 での再現 | 手元は arm64 のみ。amd64 では `google-chrome-stable` が追加で入るため、`--with-deps` が入れるフォントの顔ぶれが違いうる。`fc-match` の表が同じになるかは、amd64 の端末で建てるまで分からない | +| `containers/lfm` | `FROM nvidia/cuda:...` で base 由来ではなく、`fonts-noto-cjk` を自前で入れている(`containers/lfm/Dockerfile:23`)。同じ問題を抱えるかは未調査。抱えていれば別途起票する | +| イメージの増分の測り方 | 24 MB は稼働中のコンテナでの `du` の差で、apt のリストとキャッシュを含む。層としての増分は、実装の持ち場で `devbase build base --no-cache` の前後の `docker images` で測り直す | +| 利用者への周知 | 建て直すまで反映されないため、CHANGELOG に `devbase build base --no-cache` が要ることを書く。既に建てた人がいつ建て直すかは devbase の側から決められない | diff --git a/issues/PLAN63_base-image-rendering.md b/issues/PLAN63_base-image-rendering.md new file mode 100644 index 00000000..87c004e3 --- /dev/null +++ b/issues/PLAN63_base-image-rendering.md @@ -0,0 +1,240 @@ +# PLAN63: base イメージの日本語の描画と、文書を扱う軽量の道具 + +対象 issue: devbasex/devbase#161, devbasex/devbase#160 + +- ワークフローモード: `standard` + - 根拠: base イメージの本番の振る舞い(総称ファミリの解決先と、同梱するパッケージ)を変える。 + base はすべての派生イメージとプロジェクトの土台で、変更は再ビルドした全員に届く +- 2 件を 1 本にする理由: どちらも `containers/base/Dockerfile` の同じ apt / COPY の区画を触る。 + 分けると同じ箇所で競合し、レビューした差分と入る差分が変わる。#160 の本文も + 「#161 を先に入れるか、1 本の PR にまとめる」と書いている +- ベースブランチ: `release/v3.7.0`(release Pull Request は #212) + +## 目的 + +- **base コンテナで日本語を描いたとき、日本語のフェイスで描かれる。** 総称ファミリ + (`sans-serif` / `serif` / `monospace`)と、日本語環境でよく指定される書体名 + (`Meiryo` / `Yu Gothic` / `MS PGothic` / `Noto Sans JP`)と、イメージに無い書体名のすべてが、 + Noto CJK の JP フェイスへ解決される +- **欧文と、中国語・韓国語を明示した指定は壊さない。** `Arial` / `Times New Roman` / `Courier New` + は metric 互換のまま、`lang=zh-cn` / `lang=ko` はそれぞれの言語のフェイスのままにする +- **PDF を画像にする・調べる、OOXML を壊さずに読み書きする、欧文の字幅を正しく測る**が、 + base だけで(派生イメージも外部のサービスも使わずに)できる + +## 影響 + +| 対象 | 影響 | +| --- | --- | +| 公開インタフェース | 変わらない(CLI の引数・環境変数・コマンドは増減しない) | +| データ | 変わらない | +| 既存の振る舞い | **変わる。** base コンテナの中で描かれる文字のフェイスが変わる。イメージを建て直すまでは変わらない。CHANGELOG では #161 を Fixed、#160 を Added に書く | +| イメージのサイズ | +約 24 MB(7.09GB に対して +0.34%)。`fonts-local.conf` 自体は 0 | +| 利用者の操作 | **`devbase build base --no-cache` が要る。** `devbase up` では反映されない | + +## 前提 + +- **前提 1: base の既定は日本語にする。** 利用者は日本語話者で、扱う文書も日本語が多い。 + 中国語・韓国語は `lang` を明示したときだけそのフェイスを保つ形にする(言語を明示しない + 中国語の文書は日本語の字形で描かれる。これは意図した振る舞いで、変更前の「日本語の文書が + 中国語の字形で描かれる」を裏返したものである) +- **前提 2: `fonts-wqy-zenhei` は削除しない。** #161 が実測付きで結論している。削除しても + OS 既定の `65-nonlatin.conf` が `sans-serif` の prefer 一覧に `WenQuanYi Zen Hei` を含むため + 日本語にはならず(タイ語の Loma か IPAPGothic へ落ちる)、中国語のページを豆腐にするだけになる +- **前提 3: LibreOffice は base に入れない。** #160 の決定。展開 372〜459 MB は base の規律に + 見合わない +- **前提 4: `containers/docs` は新設しない。** #160 の表題の範囲は base への追加であり、 + 派生イメージの新設はそれを超える。#219 として起票済み(派生イメージ・使い捨てコンテナ・ + Google Slides の 3 つの比べ方と、LibreOffice の実測表を引き写してある) +- **前提 5: Playwright のブラウザと arm64 の Chromium には手を付けない。** #161 の「決めること」は + #220 として起票済み。`playwright-kit` が実行時に `uv sync` + `playwright install chromium` を + 自前で行う設計であることを確認した(`devbasex/ai-plugins` の + `plugins/playwright-kit/skills/playwright-kit-ops/templates/run.sh` ほか) +- **前提 6: CI はこの変更を検査しない。** `.github/workflows/ci.yml` にイメージを建てるジョブは + 1 つも無く、`on.pull_request.branches` は `main` だけである。**`release/v3.7.0` を base にした + Pull Request では検査ジョブが 1 件も動かない**(#216)。そのため、イメージの中でしか確かめ + られない受け入れ条件の証跡は、**手元で建てたイメージから採って Pull Request 本文へ載せる** +- **前提 7: `ENV LANG` は設定しない。** 言語のヒントに頼らず、総称ファミリの解決先そのものを + 日本語にする(`fc-match sans-serif` は `lang` を明示しなくても日本語になる)。`LANG` を + `ja_JP.UTF-8` にすると、コンテナの中のすべてのコマンドの出力・ソート順・日付の書式が変わり、 + 影響がフォントの外へ出る +- **前提 8: 追加するのは 6 パッケージだけで、`pip` は足さない。** Python パッケージが要るときは + 既にある `uv` / `uvx` で賄う(`markitdown` は `uvx` のままにする。焼き込むと展開で 200 MB 前後) + +## 対象範囲 + +含む: + +- `containers/base/fonts-local.conf`(新設)と、それを `/etc/fonts/local.conf` へ置く `COPY`、 + および `fc-cache -f` の実行(#161) +- `containers/base/Dockerfile` への 6 パッケージの追加(#160) + (`poppler-utils` / `python3-pil` / `python3-defusedxml` / `python3-lxml` / + `fonts-crosextra-carlito` / `fonts-crosextra-caladea`) +- 回帰テスト(`tests/containers/`) +- 利用者向け文書と CHANGELOG + +含まない: + +- LibreOffice の追加(前提 3) +- `containers/docs` の新設(前提 4、#219) +- Playwright のブラウザの導入方法と arm64 の Chromium(前提 5、#220) +- `fonts-wqy-zenhei` の削除(前提 2) +- `ENV LANG` の設定(前提 7) +- `pip` の追加(前提 8) +- 派生イメージ(`containers/general` / `go` / `php` / `php85` / `bi-tools` / `latex` / `trygroup`)の + Dockerfile の変更。いずれも `FROM devbase-base:latest` なので、base を建て直せば自動的に効く +- `containers/lfm` の変更。`FROM nvidia/cuda:...` で base 由来ではなく、`fonts-noto-cjk` を + 自前で入れている。同じ問題を抱えるかは未調査(下の「未確認のまま残ること」) +- 新しい型・永続データ・画面の追加(そのためクラス図・ER 図・画面遷移図を作らない) + +## 前提とする取り決め + +| 項目 | 参照先 / 決めたこと | +| --- | --- | +| プロジェクト構造 | base イメージの構成物は `containers/base/` の直下に置く。ビルドコンテキストはこのディレクトリそのもの(`lib/devbase/commands/container.py` の `_build_single_image` が `str(image_dir)` を渡す)なので、`COPY` したいファイルはここに置けば届く | +| コーディング規約 | `containers/base/Dockerfile` の既存の書き方に合わせる。`COPY --chmod=` で権限を明示し、なぜそうするかを直前のコメントに書く | +| テスト戦略 | `tests/containers/` の既存の流儀に合わせる。既定は Docker に依存しない検査(Dockerfile とスクリプトの文字列・関数の呼び出し)。Docker が要る検査は `tests/snapshot/test_restore_incremental.py` の先例(fixture の中で `shutil.which` → `docker info` → `docker image inspect` の 3 段を見て `pytest.skip`)に合わせる | + +## 境界 + +| 区分 | 内容 | +| --- | --- | +| 常に行う | 手元で全体テスト、`devbase build base --no-cache` と解決先の表の採取 | +| 確認してから行う | 前提 1(日本語を既定にする)と受け入れ条件 6 の表の確定(設計 Pull Request の承認で確かめる) | +| 行わない | LibreOffice の追加、`containers/docs` の新設、`fonts-wqy-zenhei` の削除、`ENV LANG` の設定、Playwright まわりの変更 | + +## 実装計画 + +設計は [PLAN63_base-image-rendering-design.md](PLAN63_base-image-rendering-design.md)。 +**タスクへの分解は実装の持ち場で `/ndf:implementation-plan` が行う。** ここでは触る対象だけを挙げる。 + +### 修正対象 + +- `containers/base/fonts-local.conf`(新設) +- `containers/base/Dockerfile` +- `tests/containers/test_base_dockerfile_fonts.py`(新設)、 + `tests/containers/test_base_image_font_matching.py`(新設。Docker が要る) +- `docs/user/container-operations.md`(base の説明に文書の道具とフォントの行を足す) +- `CHANGELOG.md` + +### 切り戻し手順 + +- イメージの中身の変更のみ(データ移行なし)。ブランチの revert と `devbase build base --no-cache` で戻せる +- **建て直すまで戻らない。** 利用者の手元のイメージは、建て直した時点で変わり、建て直さなければ変わらない + +## 受け入れ条件 + +**実測の基準**: 以下の「現状」はすべて、2026-09-22 に手元の `devbase-base:latest` +(Ubuntu 26.04 / arm64 / fontconfig 2.17.1 / 7.09GB)で採った。イメージを建て直さずに +確かめられるものは、稼働中のコンテナへ設定とパッケージを入れて確かめてある。 + +### フォントの解決先(#161) + +- [ ] 1. 建てた base イメージの中で `fc-match sans-serif` が `Noto Sans CJK JP` を返す + (現状: `WenQuanYi Zen Hei`) +- [ ] 2. `fc-match sans-serif:lang=ja` も `Noto Sans CJK JP` を返す(現状: `WenQuanYi Zen Hei`)。 + `fc-match -s sans-serif:lang=ja` の 1 件目も `Noto Sans CJK JP` になる + (現状: `WenQuanYi Zen Hei` → `IPAPGothic` → `Loma` の順) +- [ ] 3. `fc-match serif` が `Noto Serif CJK JP`、`fc-match monospace` が `Noto Sans Mono CJK JP` を返す + (現状: `WenQuanYi Zen Hei` / `WenQuanYi Zen Hei Mono`) +- [ ] 4. `Noto Sans JP` / `Meiryo` / `Yu Gothic` / `MS PGothic` と、イメージに無い書体名 + (`Zen Kaku Gothic New`)が、いずれも `Noto Sans CJK JP` を返す(現状: すべて `WenQuanYi Zen Hei`) +- [ ] 5. 欧文が壊れない。`Arial` → `Liberation Sans`、`Times New Roman` → `Liberation Serif`、 + `Courier New` → `Liberation Mono`、`Calibri` → `Carlito`、`Cambria` → `Caladea` +- [ ] 6. **他言語が壊れない。`lang` を明示した指定は、その言語の、しかも同じ様式(sans / serif / + 等幅)のフェイスを返す。** + + | 指定 | 返すもの | + | --- | --- | + | `sans-serif:lang=zh-cn` | `Noto Sans CJK SC` | + | `serif:lang=zh-cn` | `Noto Serif CJK SC` | + | `monospace:lang=zh-cn` | `Noto Sans Mono CJK SC` | + | `sans-serif:lang=ko` | `Noto Sans CJK KR` | + | `serif:lang=ko` | `Noto Serif CJK KR` | + | `Arial:lang=zh-cn` | `Liberation Sans`(欧文の指定は言語で変わらない) | + | `Times New Roman:lang=zh-cn` | `Liberation Serif` | + | `WenQuanYi Zen Hei`(名指し) | `WenQuanYi Zen Hei`(残る) | + +- [ ] 7. 設定は `/etc/fonts/local.conf` に置かれ、`/etc/fonts/conf.d/` には 1 つも置かれない。 + ビルドの中で `fc-cache -f` が 1 度走る + 検証: `tests/containers/` の Dockerfile の文字列検査 +- [ ] 8. `/etc/fonts/local.conf` から動かさない理由(`conf.d/99-*.conf` では効かないこと、および + その実測)が、`containers/base/fonts-local.conf` の先頭のコメントと設計文書の + 「決定の記録」の**両方**にある + 検証: `tests/containers/` の文字列検査(コメントの有無)と、設計文書の目視 + +### 文書を扱う道具(#160) + +- [ ] 9. `pdftoppm` / `pdfinfo` / `pdffonts` / `pdftocairo` が `PATH` にある +- [ ] 10. `python3 -c "import PIL, defusedxml, lxml"` が終了コード 0 +- [ ] 11. `fc-match Calibri` が `Carlito`、`fc-match Cambria` が `Caladea` を返す(受け入れ条件 5 と同じ行) +- [ ] 12. `soffice` / `libreoffice` / `pip` / `pip3` のいずれも `PATH` に無い(前提 3・8 のまま)。 + `uv` はある +- [ ] 13. 追加するのは 6 パッケージの指定だけで、依存を含めて新規に入るのは 24 パッケージ、 + イメージの増分は **約 24 MB**(7.09GB に対して +0.34%)に収まる + 検証: `devbase build base --no-cache` の前後の `docker images` の差 + +### 退行しないこと + +- [ ] 14. `uv run --locked pytest tests/ -q` が終了コード 0(既存の `tests/containers/` の 8 ファイルを含む) +- [ ] 15. `devbase build base --no-cache` が arm64 で成功する。6 パッケージは Ubuntu 26.04 の + arm64 にすべて存在する(版は下の表) +- [ ] 16. base を建て直した後、派生イメージ(`containers/general` など)を建て直すと、同じ + 解決先になる(`FROM devbase-base:latest` のため)。1 つで確かめる + +| パッケージ | 版(2026-09-22 / arm64) | +| --- | --- | +| `poppler-utils` | 26.01.0-2ubuntu0.1 | +| `python3-pil` | 12.1.1-2ubuntu1.3 | +| `python3-defusedxml` | 0.7.1-3build1 | +| `python3-lxml` | 6.0.2-1build1 | +| `fonts-crosextra-carlito` | 20230309-2 | +| `fonts-crosextra-caladea` | 20200211-2 | + +## 検証手段 + +| 項目 | 手段 | +| --- | --- | +| テスト | `uv run --locked pytest tests/ -q` | +| イメージの中の解決先 | `devbase build base --no-cache` の後、`docker run --rm --entrypoint /bin/bash devbase-base:latest -c 'fc-match ...'` の表(受け入れ条件 1〜6・9〜12)。出力を Pull Request 本文へ貼る | +| イメージのサイズ | ビルドの前後の `docker images` | +| CI | **動かない**(前提 6、#216)。`gh pr checks` の `no checks reported` と `mergeStateStatus: CLEAN` は「通った」ことを意味しない | + +## 未確認のまま残ること + +| 項目 | 内容 | +| --- | --- | +| LibreOffice と Chromium での実際の描画 | どちらも base に無いため実機未検証。確かめたのは `fc-match` の水準まで(#161 と同じ)。LibreOffice は fontconfig とは別の照合も持つ | +| `containers/lfm` | `FROM nvidia/cuda:...` で base 由来ではなく、`fonts-noto-cjk` を自前で入れている(`containers/lfm/Dockerfile:23`)。同じ問題を抱えるかは未調査。抱えていれば別途起票する | +| amd64 での再現 | 手元は arm64 のみ。amd64 では `google-chrome-stable` が追加で入るため、フォントの顔ぶれが違いうる | +## 依頼(原文) + +#161: + +> **base コンテナでは、日本語が中国語のフォントで描画される。** 総称ファミリの `sans-serif` そのものが中国語フォントへ解決される。 +> +> **影響は Office 文書の描画に限らない。** fontconfig は Chromium / Playwright のスクリーンショット、PDF の生成、画像の生成すべてが参照する。**日本語を含むページを撮ると、中国語の字形で写る。** +> +> `/etc/fonts/local.conf` を1つ置く。(…)Dockerfile へは `COPY --chmod=0644 fonts-local.conf /etc/fonts/local.conf` を足し、`fc-cache -f` を1度走らせる。**追加パッケージは要らない(サイズは 0)。** +> +> **`conf.d/99-*.conf` へ置くと効かない。** 同じ内容で実測した。 +> +> **`fonts-wqy-zenhei` を外す案は採らない。** rdepends が空なので削除自体はできるが、上の実測のとおり `65-nonlatin.conf` が残るため `sans-serif` はタイ語や IPAPGothic へ落ちるだけで、日本語にはならない。 + +#160: + +> **base コンテナに、Office 文書を画像へ描画する経路が1つも無い。**(…) +> +> **LibreOffice は base に入れない(決定)。** 展開 372〜459 MB は base の規律に見合わない。 +> +> ``` +> poppler-utils +> python3-pil python3-defusedxml python3-lxml +> fonts-crosextra-carlito fonts-crosextra-caladea +> ``` +> +> **展開 約24 MB / ダウンロード 約7 MB、7.09GB の base に対して +0.34%。** +> +> **`pip` は足さない。`uv` があるため、必要な Python パッケージはそちらで賄う。** +> +> #161 と本件は `containers/base/Dockerfile` の同じ apt / COPY の区画を触る。**#161 を先に入れるか、1 本の PR にまとめる。** + From 62755ba75dd0959e7c77d5898f6fe63dde4ffbf2 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 07:21:01 +0900 Subject: [PATCH 2/9] =?UTF-8?q?docs(PLAN63):=20=E5=8F=97=E3=81=91=E5=85=A5?= =?UTF-8?q?=E3=82=8C=E6=9D=A1=E4=BB=B6=206=20=E3=81=AE=E8=A1=A8=E3=82=92?= =?UTF-8?q?=208=20=E9=80=9A=E3=82=8A=E3=81=B8=E6=8F=83=E3=81=88=E3=80=81wq?= =?UTF-8?q?y-zenhei=20=E3=81=AE=E6=89=B1=E3=81=84=E3=82=92=E5=AE=9F?= =?UTF-8?q?=E6=85=8B=E3=81=B8=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit レビュー指摘 2 件への対応。 - 受け入れ条件 6 の表に総称ファミリ 4 つ × 言語 2 つの 8 行が揃っていなかった。 `sans:lang=zh-cn` / `sans:lang=ko` / `monospace:lang=ko` を足し、欧文を名指しした 行にも `Arial:lang=ko` を足して 12 行にした(major) - 設計文書の「解決先の表」にも同じ 4 行を足し、`serif:lang=ko` の変更前が `(未測定)` のままだったのを実測値 `WenQuanYi Zen Hei` へ直した - テスト設計の受け入れ条件 6 の行を「8 つ」から「12 行(総称ファミリ 4 つ × 言語 2 つの 8 行と、欧文を名指しした 3 行、中国語の書体を名指しした 1 行)」へ改めた - 「変えないもの」の `fonts-wqy-zenhei` の行が Dockerfile の実態と合っていなかった。 導入の行は無く `npx playwright install --with-deps chromium` が依存として入れる、 と書き直した。決定 6 の検査の観点も `apt-get remove` / `apt-get purge` / `dpkg -r` が無いことの確認へ具体化した(minor) いずれも手元の devbase-base:latest(arm64)での実測にもとづく。 Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering-design.md | 13 +++++++++---- issues/PLAN63_base-image-rendering.md | 4 ++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/issues/PLAN63_base-image-rendering-design.md b/issues/PLAN63_base-image-rendering-design.md index 47478c2e..24177d67 100644 --- a/issues/PLAN63_base-image-rendering-design.md +++ b/issues/PLAN63_base-image-rendering-design.md @@ -55,11 +55,15 @@ Ubuntu 26.04 / fontconfig 2.17.1 / イメージのサイズ 7.09GB。稼働中 | `Calibri` | WenQuanYi Zen Hei | **Carlito**(パッケージの追加による) | | `Cambria` | WenQuanYi Zen Hei | **Caladea**(同上) | | `sans-serif:lang=zh-cn` | Noto Sans CJK SC | Noto Sans CJK SC | +| `sans:lang=zh-cn` | Noto Sans CJK SC | Noto Sans CJK SC | | `serif:lang=zh-cn` | Noto Serif CJK SC | Noto Serif CJK SC | | `monospace:lang=zh-cn` | Noto Sans Mono CJK SC | Noto Sans Mono CJK SC | | `sans-serif:lang=ko` | WenQuanYi Zen Hei | **Noto Sans CJK KR** | -| `serif:lang=ko` | (未測定) | **Noto Serif CJK KR** | +| `sans:lang=ko` | WenQuanYi Zen Hei | **Noto Sans CJK KR** | +| `serif:lang=ko` | WenQuanYi Zen Hei | **Noto Serif CJK KR** | +| `monospace:lang=ko` | WenQuanYi Zen Hei Mono | **Noto Sans Mono CJK KR** | | `Arial:lang=zh-cn` | Liberation Sans | Liberation Sans | +| `Arial:lang=ko` | Liberation Sans | Liberation Sans | | `WenQuanYi Zen Hei`(名指し) | WenQuanYi Zen Hei | WenQuanYi Zen Hei | | `IPAPGothic`(名指し) | IPAPGothic | IPAPGothic | @@ -97,7 +101,8 @@ Ubuntu 26.04 / fontconfig 2.17.1 / イメージのサイズ 7.09GB。稼働中 次のものは変えない。 -- `fonts-wqy-zenhei` の導入(要求の前提 2) +- `fonts-wqy-zenhei` を削除しないこと(要求の前提 2)。Dockerfile に導入の行は無く、 + `npx playwright install --with-deps chromium` が依存として入れる - `npx playwright install --with-deps chromium` とその後のクリーンアップ(前提 5、#220) - 派生イメージの Dockerfile(すべて `FROM devbase-base:latest`) - `containers/lfm`(base 由来ではない) @@ -326,7 +331,7 @@ Playwright が後から入れるフォントを知らないキャッシュが残 | テスト | Docker | 何を固定するか | | --- | --- | --- | -| `tests/containers/test_base_dockerfile_fonts.py` | 不要 | 6 パッケージが一覧にあること。`COPY` の宛先が `/etc/fonts/local.conf` であり `conf.d/` ではないこと。`fc-cache -f` が `COPY` より後にあること。`fonts-local.conf` が整形式の XML で、4 つの `` と 8 つの `` を持つこと。先頭のコメントが置き場所の理由(`51-local.conf` / `conf.d` / `99`)に触れていること。`libreoffice` / `soffice` / `pip` を入れていないこと。`fonts-wqy-zenhei` を消していないこと | +| `tests/containers/test_base_dockerfile_fonts.py` | 不要 | 6 パッケージが一覧にあること。`COPY` の宛先が `/etc/fonts/local.conf` であり `conf.d/` ではないこと。`fc-cache -f` が `COPY` より後にあること。`fonts-local.conf` が整形式の XML で、4 つの `` と 8 つの `` を持つこと。先頭のコメントが置き場所の理由(`51-local.conf` / `conf.d` / `99`)に触れていること。`libreoffice` / `soffice` / `pip` を入れていないこと。Dockerfile に `fonts-wqy-zenhei` を対象とする `apt-get remove` / `apt-get purge` / `dpkg -r` が無いこと | | `tests/containers/test_base_image_font_matching.py` | 要る | 「解決先の表」の各行。1 回の `docker run` で全部の `fc-match` を採り、行ごとに突き合わせる | **Docker が要るテストは、`tests/snapshot/test_restore_incremental.py` の先例に合わせる。** @@ -382,7 +387,7 @@ base の利用者は日本語話者で、扱う文書も日本語が多い。`la | --- | --- | | 1・2・3・4 | `test_base_image_font_matching.py` の解決先の表(JP の行)。Docker が無い / イメージが古いときは skip | | 5・11 | 同じ表の欧文の行(`Arial` / `Times New Roman` / `Courier New` / `Calibri` / `Cambria`) | -| 6 | 同じ表の `lang` を明示した行 8 つ。**決定 2 の壊れ方を捕まえるのはこの行である** | +| 6 | 同じ表の `lang` を明示した行 12 行(総称ファミリ 4 つ × 言語 2 つの 8 行と、欧文を名指しした 3 行、中国語の書体を名指しした 1 行)。**決定 2 の壊れ方を捕まえるのはこの行である** | | 7 | `test_base_dockerfile_fonts.py`: `COPY` の宛先が `/etc/fonts/local.conf` であること、`conf.d/` を宛先にする `COPY` が無いこと、`fc-cache -f` が 1 度だけで `COPY` より後にあること | | 8 | `test_base_dockerfile_fonts.py`: `fonts-local.conf` の先頭のコメントが `51-local.conf` と `conf.d` と `99` に触れていること。設計文書の側は目視 | | 9・10・12 | `test_base_image_font_matching.py` と同じ `docker run` の中で、`command -v` と `python3 -c "import …"` の結果も採る | diff --git a/issues/PLAN63_base-image-rendering.md b/issues/PLAN63_base-image-rendering.md index 87c004e3..4a0cd925 100644 --- a/issues/PLAN63_base-image-rendering.md +++ b/issues/PLAN63_base-image-rendering.md @@ -146,11 +146,15 @@ | 指定 | 返すもの | | --- | --- | | `sans-serif:lang=zh-cn` | `Noto Sans CJK SC` | + | `sans:lang=zh-cn` | `Noto Sans CJK SC` | | `serif:lang=zh-cn` | `Noto Serif CJK SC` | | `monospace:lang=zh-cn` | `Noto Sans Mono CJK SC` | | `sans-serif:lang=ko` | `Noto Sans CJK KR` | + | `sans:lang=ko` | `Noto Sans CJK KR` | | `serif:lang=ko` | `Noto Serif CJK KR` | + | `monospace:lang=ko` | `Noto Sans Mono CJK KR` | | `Arial:lang=zh-cn` | `Liberation Sans`(欧文の指定は言語で変わらない) | + | `Arial:lang=ko` | `Liberation Sans`(同上) | | `Times New Roman:lang=zh-cn` | `Liberation Serif` | | `WenQuanYi Zen Hei`(名指し) | `WenQuanYi Zen Hei`(残る) | From 493e9d8595079664a50c16fc39d0f046e7b50425 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 07:27:44 +0900 Subject: [PATCH 3/9] =?UTF-8?q?docs(PLAN63):=20=E5=88=87=E3=82=8A=E6=88=BB?= =?UTF-8?q?=E3=81=97=E6=89=8B=E9=A0=86=E3=81=AB=E6=B4=BE=E7=94=9F=E3=82=A4?= =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=82=B8=E3=81=A8=E3=82=B3=E3=83=B3=E3=83=86?= =?UTF-8?q?=E3=83=8A=E3=81=AE=E5=BB=BA=E3=81=A6=E7=9B=B4=E3=81=97=E3=82=92?= =?UTF-8?q?=E8=B6=B3=E3=81=97=E3=80=81``=20=E3=81=AE=E6=95=B0?= =?UTF-8?q?=E3=82=92=209=20=E3=81=AB=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cross-review round 2 (codex) の 2 件へ対応した。 - 切り戻し手順を 4 段(revert / base の再ビルド / 派生イメージの建て直し / 稼働中コンテナの作り直し)へ書き直した。base のタグを戻しても、派生イメージは `FROM devbase-base:latest` を自分のビルドの時点で焼き込むため建て直すまで古い層を持つ。 「影響」の表の「利用者の操作」の行にも同じことを書いた - `fonts-local.conf` の `` は、未導入の書体の受け皿 1 つと総称ファミリ 4 つ × 言語 2 つの 8 つで合計 9 つである。テスト設計の検査条件と本文の説明の両方を 9 つへ直した Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering-design.md | 6 +++--- issues/PLAN63_base-image-rendering.md | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/issues/PLAN63_base-image-rendering-design.md b/issues/PLAN63_base-image-rendering-design.md index 24177d67..198bff8d 100644 --- a/issues/PLAN63_base-image-rendering-design.md +++ b/issues/PLAN63_base-image-rendering-design.md @@ -168,8 +168,8 @@ Ubuntu 26.04 / fontconfig 2.17.1 / イメージのサイズ 7.09GB。稼働中 ``` -規則は 4 つの ``、1 つの受け皿、そして **4 つの総称ファミリ × 2 つの言語 = 8 つ**の -`` で構成する。 +規則は 4 つの `` と 9 つの `` で構成する。`` の内訳は、未導入の書体の +受け皿が 1 つと、**総称ファミリ 4 つ × 言語 2 つ = 8 つ**である。 | 総称ファミリ | `lang=zh-cn` で前置する | `lang=ko` で前置する | | --- | --- | --- | @@ -331,7 +331,7 @@ Playwright が後から入れるフォントを知らないキャッシュが残 | テスト | Docker | 何を固定するか | | --- | --- | --- | -| `tests/containers/test_base_dockerfile_fonts.py` | 不要 | 6 パッケージが一覧にあること。`COPY` の宛先が `/etc/fonts/local.conf` であり `conf.d/` ではないこと。`fc-cache -f` が `COPY` より後にあること。`fonts-local.conf` が整形式の XML で、4 つの `` と 8 つの `` を持つこと。先頭のコメントが置き場所の理由(`51-local.conf` / `conf.d` / `99`)に触れていること。`libreoffice` / `soffice` / `pip` を入れていないこと。Dockerfile に `fonts-wqy-zenhei` を対象とする `apt-get remove` / `apt-get purge` / `dpkg -r` が無いこと | +| `tests/containers/test_base_dockerfile_fonts.py` | 不要 | 6 パッケージが一覧にあること。`COPY` の宛先が `/etc/fonts/local.conf` であり `conf.d/` ではないこと。`fc-cache -f` が `COPY` より後にあること。`fonts-local.conf` が整形式の XML で、4 つの `` と 9 つの ``(受け皿 1 つと、総称ファミリ 4 つ × 言語 2 つの 8 つ)を持つこと。先頭のコメントが置き場所の理由(`51-local.conf` / `conf.d` / `99`)に触れていること。`libreoffice` / `soffice` / `pip` を入れていないこと。Dockerfile に `fonts-wqy-zenhei` を対象とする `apt-get remove` / `apt-get purge` / `dpkg -r` が無いこと | | `tests/containers/test_base_image_font_matching.py` | 要る | 「解決先の表」の各行。1 回の `docker run` で全部の `fc-match` を採り、行ごとに突き合わせる | **Docker が要るテストは、`tests/snapshot/test_restore_incremental.py` の先例に合わせる。** diff --git a/issues/PLAN63_base-image-rendering.md b/issues/PLAN63_base-image-rendering.md index 4a0cd925..ec1b935f 100644 --- a/issues/PLAN63_base-image-rendering.md +++ b/issues/PLAN63_base-image-rendering.md @@ -29,7 +29,7 @@ | データ | 変わらない | | 既存の振る舞い | **変わる。** base コンテナの中で描かれる文字のフェイスが変わる。イメージを建て直すまでは変わらない。CHANGELOG では #161 を Fixed、#160 を Added に書く | | イメージのサイズ | +約 24 MB(7.09GB に対して +0.34%)。`fonts-local.conf` 自体は 0 | -| 利用者の操作 | **`devbase build base --no-cache` が要る。** `devbase up` では反映されない | +| 利用者の操作 | **`devbase build base --no-cache` が要る。`devbase up` では反映されない。** 派生イメージ(`containers/general` など)を使っているプロジェクトは、その派生イメージも建て直す。稼働中のコンテナは作り直す(`devbase rebuild`) | ## 前提 @@ -118,8 +118,17 @@ ### 切り戻し手順 -- イメージの中身の変更のみ(データ移行なし)。ブランチの revert と `devbase build base --no-cache` で戻せる -- **建て直すまで戻らない。** 利用者の手元のイメージは、建て直した時点で変わり、建て直さなければ変わらない +- データの移行は無い。変わるのはイメージの中身だけである +- 戻すには 4 つが要る。 + 1. ブランチの revert + 2. `devbase build base --no-cache` + 3. **base から派生したイメージの建て直し**(`containers/general` / `go` / `php` / `php85` / + `bi-tools` / `latex` / `trygroup` のうち使っているもの)。派生イメージは + `FROM devbase-base:latest` を自分のビルドの時点で焼き込むため、base のタグを戻しても + 建て直すまで古い層を持つ + 4. **稼働中のコンテナの作り直し**(`devbase rebuild`、または `devbase down` → `devbase up`)。 + 既に起動しているコンテナは、イメージを建て直しただけでは入れ替わらない +- **建て直すまで戻らない。** 同じことが適用の側にも当たる(受け入れ条件 16) ## 受け入れ条件 From 133cc2fcb69aa7e270e6faabb950c4494327058e Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 07:39:28 +0900 Subject: [PATCH 4/9] =?UTF-8?q?docs(PLAN63):=20=E8=A7=A3=E6=B1=BA=E5=85=88?= =?UTF-8?q?=E3=81=AE=E8=A1=A8=E3=81=AE=E6=BC=8F=E3=82=8C=E3=83=BB=E5=8F=82?= =?UTF-8?q?=E7=85=A7=E5=85=88=E3=83=BB=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A5=E3=81=AE=E8=AA=AC=E6=98=8E=E3=82=92=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit round 3 のレビュー指摘 3 件へ対応する。 - 解決先の表へ `Times New Roman:lang=zh-cn` の行を足す(実測済みの値。 `Arial:lang=ko` は既にあった) - 決定 2 の中の参照先を「決定 4」から「決定 8」へ直す。`sans-serif` の prefer を日本語へ向けるのは決定 8 である - 決定 4 のキャッシュの説明を Docker の仕様へ合わせる。`RUN` のキャッシュが 効いている間はその中身が実行されないため、取得先の内容が変わっても キャッシュは無効にならない Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering-design.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/issues/PLAN63_base-image-rendering-design.md b/issues/PLAN63_base-image-rendering-design.md index 198bff8d..0976b674 100644 --- a/issues/PLAN63_base-image-rendering-design.md +++ b/issues/PLAN63_base-image-rendering-design.md @@ -63,6 +63,7 @@ Ubuntu 26.04 / fontconfig 2.17.1 / イメージのサイズ 7.09GB。稼働中 | `serif:lang=ko` | WenQuanYi Zen Hei | **Noto Serif CJK KR** | | `monospace:lang=ko` | WenQuanYi Zen Hei Mono | **Noto Sans Mono CJK KR** | | `Arial:lang=zh-cn` | Liberation Sans | Liberation Sans | +| `Times New Roman:lang=zh-cn` | Liberation Serif | Liberation Serif | | `Arial:lang=ko` | Liberation Sans | Liberation Sans | | `WenQuanYi Zen Hei`(名指し) | WenQuanYi Zen Hei | WenQuanYi Zen Hei | | `IPAPGothic`(名指し) | IPAPGothic | IPAPGothic | @@ -269,7 +270,7 @@ graph TD **規則を 1 つも置かない案も採らない。** `zh-cn` は OS 既定の `65-nonlatin.conf` と `70-fonts-noto-cjk.conf` が正しく扱うので規則なしでも合うが、**`ko` は合わない** -(決定 4 の `sans-serif` の prefer が JP を先頭にするため、韓国語が日本語の字形になる)。 +(決定 8 の `sans-serif` の prefer が JP を先頭にするため、韓国語が日本語の字形になる)。 `ko` だけ書くと非対称で、なぜ `zh-cn` が無いのかが後から読めない。8 つ並べて対称にする。 ### 決定 3: 未導入の書体の受け皿は、弱い結合の `append` 1 つで足りる @@ -302,8 +303,13 @@ Dockerfile の 1 つ目の `RUN` は `apt-get install` を 2 回呼ぶ。1 回 | `apt-get update` をもう 1 回走らせる | 1 つ目の `RUN` は最後に `rm -rf /var/lib/apt/lists/*` でリストを消す。新しい `RUN` はリストを取り直す必要がある | | クリーンアップを書き写す | `apt-get clean` と `rm -rf` を 2 か所に持つことになる | -**この 6 つが後から変わる頻度は低い。** 一方 1 つ目の `RUN` は、NodeSource の設定スクリプトを -`curl | bash` で毎回取りに行くなど、外からの変化でどのみち無効になりうる。層を増やして +**キャッシュが無効になる条件は、その `RUN` の命令の文字列が変わるか、親の層が変わるか、 +`--no-cache` を渡すかの 3 つである。** 取得先の中身が変わってもキャッシュは無効にならない +(`curl | bash` は、キャッシュが効いている間は実行されない)。 + +**この変更そのものが 1 つ目の `RUN` の文字列を変えるため、層を分けても分けなくても、この +変更では 1 度建て直される。** 層を分けて守れるのは「次にこの 6 つを足し引きしたとき」だけで、 +その頻度は低い。`devbase build base --no-cache` はどのみち全部を建て直す。層を増やして 守るほどの利得が無い。 ### 決定 5: `COPY` と `fc-cache -f` は末尾の `COPY` 群へ置く From 7cf8a34399a53f91da38c058b674df4e9a2f474c Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 07:46:53 +0900 Subject: [PATCH 5/9] =?UTF-8?q?docs(PLAN63):=20=E5=8F=97=E3=81=91=E5=85=A5?= =?UTF-8?q?=E3=82=8C=E6=9D=A1=E4=BB=B6=2013=20=E3=81=AE=E5=90=88=E5=90=A6?= =?UTF-8?q?=E3=81=AE=E3=83=A9=E3=82=A4=E3=83=B3=E3=82=92=E6=B8=AC=E3=82=8A?= =?UTF-8?q?=E6=96=B9=E3=81=AE=E9=81=95=E3=81=84=E3=81=AB=E8=80=90=E3=81=88?= =?UTF-8?q?=E3=82=8B=E5=BD=A2=E3=81=B8=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 受け入れ条件 13 は合否のラインを「約 24 MB」とし、検証を `docker images` の差と定めていた。しかし 24 MB は稼働中のコンテナでの `du` の差(apt の リストとキャッシュを含む)で、`docker images` が出す層単位の値とは測り方が 違う。そのままでは正しい実装でも不合格になりうる。 合否のラインを「7.09GB に対して +0.5% 未満(40 MB 以下)」へ改め、 24 MB をラインに使わない理由を条件の中に書いた。設計文書の 「未確認のまま残ること」にも、このラインが測り方の違いを吸収するための ものである旨を追記した。 Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering-design.md | 2 +- issues/PLAN63_base-image-rendering.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/issues/PLAN63_base-image-rendering-design.md b/issues/PLAN63_base-image-rendering-design.md index 0976b674..49a8703c 100644 --- a/issues/PLAN63_base-image-rendering-design.md +++ b/issues/PLAN63_base-image-rendering-design.md @@ -413,5 +413,5 @@ base の利用者は日本語話者で、扱う文書も日本語が多い。`la | LibreOffice と Chromium での実際の描画 | どちらも base に無いため実機未検証。確かめたのは `fc-match` の水準まで。LibreOffice は fontconfig とは別の照合も持つため、解決先が `fc-match` と一致しないことがある(#161 の由来になった `volareinc/nyle-dx` PR #5 では `NotoSansCJKsc` が埋め込まれていた)。`containers/docs`(#219)を作るときに、そこで確かめる | | amd64 での再現 | 手元は arm64 のみ。amd64 では `google-chrome-stable` が追加で入るため、`--with-deps` が入れるフォントの顔ぶれが違いうる。`fc-match` の表が同じになるかは、amd64 の端末で建てるまで分からない | | `containers/lfm` | `FROM nvidia/cuda:...` で base 由来ではなく、`fonts-noto-cjk` を自前で入れている(`containers/lfm/Dockerfile:23`)。同じ問題を抱えるかは未調査。抱えていれば別途起票する | -| イメージの増分の測り方 | 24 MB は稼働中のコンテナでの `du` の差で、apt のリストとキャッシュを含む。層としての増分は、実装の持ち場で `devbase build base --no-cache` の前後の `docker images` で測り直す | +| イメージの増分の測り方 | 24 MB は稼働中のコンテナでの `du` の差で、apt のリストとキャッシュを含む。層としての増分は、実装の持ち場で `devbase build base --no-cache` の前後の `docker images` で測り直す。受け入れ条件 13 の合否のラインは、測り方の違いを吸収できるよう +0.5% 未満(40 MB 以下)にしてある。 | | 利用者への周知 | 建て直すまで反映されないため、CHANGELOG に `devbase build base --no-cache` が要ることを書く。既に建てた人がいつ建て直すかは devbase の側から決められない | diff --git a/issues/PLAN63_base-image-rendering.md b/issues/PLAN63_base-image-rendering.md index ec1b935f..acce09ad 100644 --- a/issues/PLAN63_base-image-rendering.md +++ b/issues/PLAN63_base-image-rendering.md @@ -183,8 +183,12 @@ - [ ] 12. `soffice` / `libreoffice` / `pip` / `pip3` のいずれも `PATH` に無い(前提 3・8 のまま)。 `uv` はある - [ ] 13. 追加するのは 6 パッケージの指定だけで、依存を含めて新規に入るのは 24 パッケージ、 - イメージの増分は **約 24 MB**(7.09GB に対して +0.34%)に収まる + **イメージの増分が 7.09GB に対して +0.5% 未満(40 MB 以下)**に収まる 検証: `devbase build base --no-cache` の前後の `docker images` の差 + **合否のラインを「約 24 MB」にしない。** 24 MB は稼働中のコンテナでの `du` の差で、 + apt のリストとキャッシュを含む測り方である。`docker images` が出すのは層単位の値で、 + 層の重なりの分だけずれる。同じ数字を 2 つの測り方で比べると、正しい実装でも + 不合格になりうる ### 退行しないこと From c64b0604277646c528db159d514cd4a490b3aebc Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 07:52:22 +0900 Subject: [PATCH 6/9] =?UTF-8?q?docs(PLAN63):=20=E3=82=B3=E3=83=B3=E3=83=86?= =?UTF-8?q?=E3=83=8A=E3=81=AE=E4=BD=9C=E3=82=8A=E7=9B=B4=E3=81=97=E3=81=AE?= =?UTF-8?q?=E6=A1=88=E5=86=85=E3=82=92=20devbase=20down=20=E2=86=92=20up?= =?UTF-8?q?=20=E3=81=B8=E7=B5=B1=E4=B8=80=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devbase rebuild は devbase build --expires=7 のシノニム (lib/devbase/commands/container.py の cmd_rebuild) で、イメージのビルドしか 行わず稼働中のコンテナを作り直さない。期限内ならビルドそのものを飛ばすため、 切り戻しにも適用にも使えない。 「影響」の表の「利用者の操作」と「切り戻し手順」の 4 番目の両方で、稼働中の コンテナの作り直しを devbase down → devbase up に統一し、devbase rebuild が ここでは使えない理由を書き添えた。 Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/issues/PLAN63_base-image-rendering.md b/issues/PLAN63_base-image-rendering.md index acce09ad..eae29641 100644 --- a/issues/PLAN63_base-image-rendering.md +++ b/issues/PLAN63_base-image-rendering.md @@ -29,7 +29,7 @@ | データ | 変わらない | | 既存の振る舞い | **変わる。** base コンテナの中で描かれる文字のフェイスが変わる。イメージを建て直すまでは変わらない。CHANGELOG では #161 を Fixed、#160 を Added に書く | | イメージのサイズ | +約 24 MB(7.09GB に対して +0.34%)。`fonts-local.conf` 自体は 0 | -| 利用者の操作 | **`devbase build base --no-cache` が要る。`devbase up` では反映されない。** 派生イメージ(`containers/general` など)を使っているプロジェクトは、その派生イメージも建て直す。稼働中のコンテナは作り直す(`devbase rebuild`) | +| 利用者の操作 | **`devbase build base --no-cache` が要る。`devbase up` だけでは反映されない。** 派生イメージ(`containers/general` など)を使っているプロジェクトは、その派生イメージも建て直す。稼働中のコンテナは `devbase down` → `devbase up` で作り直す | ## 前提 @@ -126,8 +126,11 @@ `bi-tools` / `latex` / `trygroup` のうち使っているもの)。派生イメージは `FROM devbase-base:latest` を自分のビルドの時点で焼き込むため、base のタグを戻しても 建て直すまで古い層を持つ - 4. **稼働中のコンテナの作り直し**(`devbase rebuild`、または `devbase down` → `devbase up`)。 - 既に起動しているコンテナは、イメージを建て直しただけでは入れ替わらない + 4. **稼働中のコンテナの作り直し**(`devbase down` → `devbase up`)。既に起動している + コンテナは、イメージを建て直しただけでは入れ替わらない。**`devbase rebuild` はここでは + 使えない。** `devbase build --expires=7` のシノニム(`lib/devbase/commands/container.py` + の `cmd_rebuild`)で、イメージのビルドしか行わずコンテナを作り直さないうえ、期限内なら + ビルドそのものを飛ばす - **建て直すまで戻らない。** 同じことが適用の側にも当たる(受け入れ条件 16) ## 受け入れ条件 From a9081d1ee6ad037f4c971bccb63b07503ddb77b3 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 08:02:49 +0900 Subject: [PATCH 7/9] =?UTF-8?q?docs(PLAN63):=20=E5=8F=97=E3=81=91=E5=85=A5?= =?UTF-8?q?=E3=82=8C=E6=9D=A1=E4=BB=B6=E3=81=B8=20IPAPGothic=20=E3=81=A8?= =?UTF-8?q?=20fc-match=20sans=20=E3=82=92=E8=B6=B3=E3=81=97=E3=80=81?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E8=A8=AD=E8=A8=88=E3=81=AB=20fc-mat?= =?UTF-8?q?ch=20-s=20=E3=82=92=E6=9B=B8=E3=81=8F=20(#221)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit round 6 のレビュー指摘 3 件への対応。 - 受け入れ条件 6 の表へ `IPAPGothic`(名指し)の行を足した。イメージに実在する 日本語の書体がそのまま残ることを固定する。受け入れ条件 4 は「イメージに無い 書体名が JP へ落ちる」条件なので、`WenQuanYi Zen Hei`(名指し)と同じ性質の この行は 6 の表へ置いた - 受け入れ条件 1 へ `fc-match sans`(`lang` なし)を足した。`fonts-local.conf` は `sans` の `` を持つのに、`lang` を付けない照合が固定されていなかった - テスト設計の受け入れ条件 1〜4 の行へ、`fc-match -s sans-serif:lang=ja` の 1 件目も 確かめることを明記した。あわせて受け入れ条件 6 の行数を 12 から 13 へ直した Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering-design.md | 4 ++-- issues/PLAN63_base-image-rendering.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/issues/PLAN63_base-image-rendering-design.md b/issues/PLAN63_base-image-rendering-design.md index 49a8703c..d1c96a39 100644 --- a/issues/PLAN63_base-image-rendering-design.md +++ b/issues/PLAN63_base-image-rendering-design.md @@ -391,9 +391,9 @@ base の利用者は日本語話者で、扱う文書も日本語が多い。`la | 受け入れ条件 | 何で確かめるか | | --- | --- | -| 1・2・3・4 | `test_base_image_font_matching.py` の解決先の表(JP の行)。Docker が無い / イメージが古いときは skip | +| 1・2・3・4 | `test_base_image_font_matching.py` の解決先の表(JP の行)。受け入れ条件 2 は `fc-match` の 1 件だけでなく、**`fc-match -s sans-serif:lang=ja` の 1 件目が `Noto Sans CJK JP` であること**も確かめる(同じ `docker run` の中で `fc-match -s` の出力の先頭を採る)。Docker が無い / イメージが古いときは skip | | 5・11 | 同じ表の欧文の行(`Arial` / `Times New Roman` / `Courier New` / `Calibri` / `Cambria`) | -| 6 | 同じ表の `lang` を明示した行 12 行(総称ファミリ 4 つ × 言語 2 つの 8 行と、欧文を名指しした 3 行、中国語の書体を名指しした 1 行)。**決定 2 の壊れ方を捕まえるのはこの行である** | +| 6 | 同じ表の `lang` を明示した行と、書体を名指しした行の 13 行(総称ファミリ 4 つ × 言語 2 つの 8 行、欧文を名指しした 3 行、実在する書体を名指しした 2 行(`WenQuanYi Zen Hei` / `IPAPGothic`))。**決定 2 の壊れ方を捕まえるのはこの行である** | | 7 | `test_base_dockerfile_fonts.py`: `COPY` の宛先が `/etc/fonts/local.conf` であること、`conf.d/` を宛先にする `COPY` が無いこと、`fc-cache -f` が 1 度だけで `COPY` より後にあること | | 8 | `test_base_dockerfile_fonts.py`: `fonts-local.conf` の先頭のコメントが `51-local.conf` と `conf.d` と `99` に触れていること。設計文書の側は目視 | | 9・10・12 | `test_base_image_font_matching.py` と同じ `docker run` の中で、`command -v` と `python3 -c "import …"` の結果も採る | diff --git a/issues/PLAN63_base-image-rendering.md b/issues/PLAN63_base-image-rendering.md index eae29641..9d30087d 100644 --- a/issues/PLAN63_base-image-rendering.md +++ b/issues/PLAN63_base-image-rendering.md @@ -142,7 +142,7 @@ ### フォントの解決先(#161) - [ ] 1. 建てた base イメージの中で `fc-match sans-serif` が `Noto Sans CJK JP` を返す - (現状: `WenQuanYi Zen Hei`) + (現状: `WenQuanYi Zen Hei`)。`fc-match sans` も同じ(現状: `WenQuanYi Zen Hei`) - [ ] 2. `fc-match sans-serif:lang=ja` も `Noto Sans CJK JP` を返す(現状: `WenQuanYi Zen Hei`)。 `fc-match -s sans-serif:lang=ja` の 1 件目も `Noto Sans CJK JP` になる (現状: `WenQuanYi Zen Hei` → `IPAPGothic` → `Loma` の順) @@ -169,6 +169,7 @@ | `Arial:lang=ko` | `Liberation Sans`(同上) | | `Times New Roman:lang=zh-cn` | `Liberation Serif` | | `WenQuanYi Zen Hei`(名指し) | `WenQuanYi Zen Hei`(残る) | + | `IPAPGothic`(名指し) | `IPAPGothic`(残る。イメージに実在する日本語の書体を奪わない) | - [ ] 7. 設定は `/etc/fonts/local.conf` に置かれ、`/etc/fonts/conf.d/` には 1 つも置かれない。 ビルドの中で `fc-cache -f` が 1 度走る From e6a83987c81e6fb61434485c74a70c1547a1f16f Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 08:14:56 +0900 Subject: [PATCH 8/9] =?UTF-8?q?docs(PLAN63):=20=E5=8F=97=E3=81=91=E5=85=A5?= =?UTF-8?q?=E3=82=8C=E6=9D=A1=E4=BB=B6=205=20=E3=81=8B=E3=82=89=20Calibri?= =?UTF-8?q?=20/=20Cambria=20=E3=82=92=E5=A4=96=E3=81=97=2011=20=E3=81=B8?= =?UTF-8?q?=E7=A7=BB=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 受け入れ条件 5 の見出しは「欧文が壊れない」だが、`Calibri` と `Cambria` は 変更前に `WenQuanYi Zen Hei`(中国語のフェイス)へ落ちており、`Arial` などの 「変更の前後で変わらない」ものとは性質が違う。同じ枠に並べると基準を誤読させる。 - 受け入れ条件 5 は変わらない 3 つ(`Arial` / `Times New Roman` / `Courier New`)に絞る - 受け入れ条件 11 を「欧文の metric 互換が直る」とし、現状がどちらも `WenQuanYi Zen Hei` であることと、直す手段(`fonts-crosextra-carlito` / `fonts-crosextra-caladea`)を書く - 設計文書のテスト設計の表の `5・11` の行を、上の切り分けに合わせる 受け入れ条件の番号は 1〜16 のまま変えていない。 Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering-design.md | 2 +- issues/PLAN63_base-image-rendering.md | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/issues/PLAN63_base-image-rendering-design.md b/issues/PLAN63_base-image-rendering-design.md index d1c96a39..5e80f6ce 100644 --- a/issues/PLAN63_base-image-rendering-design.md +++ b/issues/PLAN63_base-image-rendering-design.md @@ -392,7 +392,7 @@ base の利用者は日本語話者で、扱う文書も日本語が多い。`la | 受け入れ条件 | 何で確かめるか | | --- | --- | | 1・2・3・4 | `test_base_image_font_matching.py` の解決先の表(JP の行)。受け入れ条件 2 は `fc-match` の 1 件だけでなく、**`fc-match -s sans-serif:lang=ja` の 1 件目が `Noto Sans CJK JP` であること**も確かめる(同じ `docker run` の中で `fc-match -s` の出力の先頭を採る)。Docker が無い / イメージが古いときは skip | -| 5・11 | 同じ表の欧文の行(`Arial` / `Times New Roman` / `Courier New` / `Calibri` / `Cambria`) | +| 5・11 | 同じ表の欧文の行。5 は変更の前後で変わらない 3 つ(`Arial` / `Times New Roman` / `Courier New`)、11 は変更で直る 2 つ(`Calibri` → Carlito / `Cambria` → Caladea。**現状はどちらも `WenQuanYi Zen Hei`**) | | 6 | 同じ表の `lang` を明示した行と、書体を名指しした行の 13 行(総称ファミリ 4 つ × 言語 2 つの 8 行、欧文を名指しした 3 行、実在する書体を名指しした 2 行(`WenQuanYi Zen Hei` / `IPAPGothic`))。**決定 2 の壊れ方を捕まえるのはこの行である** | | 7 | `test_base_dockerfile_fonts.py`: `COPY` の宛先が `/etc/fonts/local.conf` であること、`conf.d/` を宛先にする `COPY` が無いこと、`fc-cache -f` が 1 度だけで `COPY` より後にあること | | 8 | `test_base_dockerfile_fonts.py`: `fonts-local.conf` の先頭のコメントが `51-local.conf` と `conf.d` と `99` に触れていること。設計文書の側は目視 | diff --git a/issues/PLAN63_base-image-rendering.md b/issues/PLAN63_base-image-rendering.md index 9d30087d..9d751978 100644 --- a/issues/PLAN63_base-image-rendering.md +++ b/issues/PLAN63_base-image-rendering.md @@ -150,8 +150,11 @@ (現状: `WenQuanYi Zen Hei` / `WenQuanYi Zen Hei Mono`) - [ ] 4. `Noto Sans JP` / `Meiryo` / `Yu Gothic` / `MS PGothic` と、イメージに無い書体名 (`Zen Kaku Gothic New`)が、いずれも `Noto Sans CJK JP` を返す(現状: すべて `WenQuanYi Zen Hei`) -- [ ] 5. 欧文が壊れない。`Arial` → `Liberation Sans`、`Times New Roman` → `Liberation Serif`、 - `Courier New` → `Liberation Mono`、`Calibri` → `Carlito`、`Cambria` → `Caladea` +- [ ] 5. **欧文が壊れない**(変更の前後で変わらない)。`Arial` → `Liberation Sans`、 + `Times New Roman` → `Liberation Serif`、`Courier New` → `Liberation Mono`。 + **`Calibri` と `Cambria` はここに含めない。** この 2 つは変更前に + `WenQuanYi Zen Hei`(中国語のフェイス)へ落ちており、「変わらない」ではなく + 「直る」ものである。受け入れ条件 11 で固定する - [ ] 6. **他言語が壊れない。`lang` を明示した指定は、その言語の、しかも同じ様式(sans / serif / 等幅)のフェイスを返す。** @@ -183,7 +186,9 @@ - [ ] 9. `pdftoppm` / `pdfinfo` / `pdffonts` / `pdftocairo` が `PATH` にある - [ ] 10. `python3 -c "import PIL, defusedxml, lxml"` が終了コード 0 -- [ ] 11. `fc-match Calibri` が `Carlito`、`fc-match Cambria` が `Caladea` を返す(受け入れ条件 5 と同じ行) +- [ ] 11. **欧文の metric 互換が直る。** `fc-match Calibri` が `Carlito`、`fc-match Cambria` が + `Caladea` を返す(**現状はどちらも `WenQuanYi Zen Hei`**)。`fonts-crosextra-carlito` と + `fonts-crosextra-caladea` を足すことで直る - [ ] 12. `soffice` / `libreoffice` / `pip` / `pip3` のいずれも `PATH` に無い(前提 3・8 のまま)。 `uv` はある - [ ] 13. 追加するのは 6 パッケージの指定だけで、依存を含めて新規に入るのは 24 パッケージ、 From cd4f4b54eb84f95c26d394acd258e66f265c42fc Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Tue, 22 Sep 2026 08:20:22 +0900 Subject: [PATCH 9/9] =?UTF-8?q?docs(PLAN63):=20=E6=B1=BA=E5=AE=9A=202=20?= =?UTF-8?q?=E3=81=AE=E8=A1=A8=E3=81=8C=E3=81=A9=E3=81=AE=E7=8A=B6=E6=85=8B?= =?UTF-8?q?=E3=82=92=E6=B8=AC=E3=81=A3=E3=81=A6=E3=81=84=E3=82=8B=E3=81=8B?= =?UTF-8?q?=E3=82=92=E5=88=97=E3=81=AE=E8=A6=8B=E5=87=BA=E3=81=97=E3=81=A8?= =?UTF-8?q?=E5=89=8D=E7=BD=AE=E3=81=8D=E3=81=A7=E7=A4=BA=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 「解決先の表」の ko の「変更前」と、決定 2 の表の ko の「規則なし」は 食い違って見えるが、測っている状態が違う。前者は /etc/fonts/local.conf が無い今の base イメージそのまま(WenQuanYi Zen Hei)、後者は local.conf は置いたうえで zh-cn / ko の だけを書かなかった状態 (Noto Sans CJK JP / Noto Serif CJK JP)である。どちらも 2026-09-22 に arm64 で実測した値なので、値は変えない。 読み分けられるように、決定 2 の表の列の見出しを「zh-cn / ko の規則なし」 へ改め、表の直前に 3 つの列がどれも local.conf を置いた状態であることと、 local.conf を置く前の値は「解決先の表」の「変更前」にあることを書いた。 決定 2 の末尾の段落の言い回しも同じ語に揃えた。 Co-Authored-By: Claude Opus 5 (1M context) --- issues/PLAN63_base-image-rendering-design.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/issues/PLAN63_base-image-rendering-design.md b/issues/PLAN63_base-image-rendering-design.md index 5e80f6ce..e5acd3b8 100644 --- a/issues/PLAN63_base-image-rendering-design.md +++ b/issues/PLAN63_base-image-rendering-design.md @@ -253,7 +253,14 @@ graph TD 3 つの変種を同じイメージの中で比べた(2026-09-22、arm64)。 -| 指定 | 規則なし | #161 の形(`lang` だけ) | この設計(`family` + `lang`) | +**3 つの列はどれも `/etc/fonts/local.conf` を置いた状態である。** 違うのは zh-cn / ko の +`` の書き方だけで、4 つの `` と受け皿はどの列にもある。**`local.conf` を置く +前の値(今の base イメージそのまま)は、この表ではなく「解決先の表」の「変更前」の列にある。** +`local.conf` が無ければ `sans-serif` 自体が `WenQuanYi Zen Hei` なので、`lang=ko` も +`WenQuanYi Zen Hei` になる。`` を置いてはじめて、ko の指定が日本語のフェイスへ +引き寄せられる ── それがこの表の 1 列目である。 + +| 指定 | zh-cn / ko の規則なし | #161 の形(`lang` だけ) | この設計(`family` + `lang`) | | --- | --- | --- | --- | | `sans-serif:lang=zh-cn` | Noto Sans CJK SC | Noto Sans CJK SC | Noto Sans CJK SC | | `serif:lang=zh-cn` | Noto Serif CJK SC | **Noto Sans CJK SC** ← 様式が崩れる | Noto Serif CJK SC | @@ -268,9 +275,10 @@ graph TD 指定を含む ── から、指定した書体を奪う。Chromium は中国語のページで `lang=zh-cn` を 載せるため、これは絵に描いた話ではない。 -**規則を 1 つも置かない案も採らない。** `zh-cn` は OS 既定の `65-nonlatin.conf` と -`70-fonts-noto-cjk.conf` が正しく扱うので規則なしでも合うが、**`ko` は合わない** -(決定 8 の `sans-serif` の prefer が JP を先頭にするため、韓国語が日本語の字形になる)。 +**zh-cn / ko の規則を 1 つも置かない案も採らない。** `zh-cn` は OS 既定の +`65-nonlatin.conf` と `70-fonts-noto-cjk.conf` が正しく扱うので規則なしでも合うが、 +**`ko` は合わない**(決定 8 の `sans-serif` の prefer が JP を先頭にするため、韓国語が +日本語の字形になる。これは `local.conf` を置いたことで初めて起きる)。 `ko` だけ書くと非対称で、なぜ `zh-cn` が無いのかが後から読めない。8 つ並べて対称にする。 ### 決定 3: 未導入の書体の受け皿は、弱い結合の `append` 1 つで足りる