Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
## [Unreleased]

### Added
- **base イメージに、Office 文書・PDF を扱う軽量の道具を足しました(PLAN63 / #160)。**
`poppler-utils`(`pdftoppm` / `pdfinfo` / `pdffonts` / `pdftocairo`)、`python3-pil`、
`python3-defusedxml`、`python3-lxml` で、PDF を画像にする・調べる、OOXML を壊さずに
読み書きすることが base だけでできます。あわせて欧文の metric 互換の
`fonts-crosextra-carlito` / `fonts-crosextra-caladea` を入れ、`Calibri` / `Cambria` の
指定が正しい字幅の書体(Carlito / Caladea)へ解決されるようにしました(これまでは
どちらも中国語フォントへ落ちていました)。**LibreOffice と `pip` は入れていません。**
Python パッケージが要るときは既にある `uv` / `uvx` を使ってください。
**反映には `devbase build base --no-cache` が要ります。**
- **名前の形に合わないプロジェクト(`_foo` など)が `projects/` に載る時点で、警告を 1 行出すように
しました(PLAN66 / #203)。** `devbase plugin install` / `update` / `sync` は、プラグインの
プロジェクト・衝突のときに合成する別名 `<名前>.<owner>`・`projects/` 直下の実ディレクトリの
Expand All @@ -19,6 +28,19 @@
それ以外に受け付ける名前と、エラーの文言は変わりません。

### Fixed
- **base コンテナで日本語が中国語のフォントで描画される問題を直しました(PLAN63 / #161)。**
総称ファミリ(`sans-serif` / `sans` / `serif` / `monospace`)と、イメージに無い書体名
(`Meiryo` / `Yu Gothic` / `MS PGothic` / `Noto Sans JP` など)が、Noto CJK の **JP**
フェイスへ解決されるようになります。これまでは `sans-serif` そのものが中国語フォント
(WenQuanYi Zen Hei)へ解決され、Chromium / Playwright のスクリーンショット・PDF の生成・
画像の生成のすべてが中国語の字形で写っていました。欧文(`Arial` / `Times New Roman` /
`Courier New`)は Liberation の metric 互換のままで、`lang=zh-cn` / `lang=ko` を明示した
指定は、その言語の、しかも同じ様式(sans / serif / 等幅)のフェイスのままです。
**言語を明示しない中国語は日本語の字形で描かれるようになります**(意図した変更です)。
設定は `/etc/fonts/local.conf` に置いており、個人の `~/.config/fontconfig/fonts.conf` で
上書きできます。**反映には `devbase build base --no-cache` が要ります。** `devbase up`
だけでは変わらず、派生イメージ(`general` など)を使っているプロジェクトは、その派生
イメージも建て直してください。
- **`group_aliases` のある置き場で、機密の参照の見出しがグループの読み替えの前と後を出すように
しました(PLAN64 / #188)。** `devbase env list` の節の見出しと件数の行、`devbase env backend test`
の参照ごとの行、`devbase env backend migrate` の移行の計画の一覧と `--to age` の完了後の一覧が、
Expand Down
21 changes: 20 additions & 1 deletion containers/base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
openssh-client \
curl ca-certificates gnupg lsb-release \
libnss3 libxrandr2 libxss1 \
fonts-noto-cjk fonts-noto-cjk-extra; \
fonts-noto-cjk fonts-noto-cjk-extra \
# 欧文の metric 互換 (Calibri -> Carlito / Cambria -> Caladea)。
# 30-metric-aliases.conf が既に対応を持っており、実体が無いと中国語フォントへ落ちる。
fonts-crosextra-carlito fonts-crosextra-caladea \
# 文書を扱う軽量の道具 (#160)。PDF を画像にする・調べる、OOXML を壊さずに読み書きする。
# LibreOffice (展開 372〜459MB) と pip は入れない。Python パッケージは uv / uvx で賄う。
poppler-utils python3-pil python3-defusedxml python3-lxml; \
# ロケール設定
locale-gen en_US.UTF-8; \
update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8; \
Expand Down Expand Up @@ -250,6 +256,19 @@ COPY --chmod=0755 tmux-clean /usr/local/bin/tmux-clean
RUN sudo ln -sf tmux-first /usr/local/bin/tmux1 \
&& sudo ln -sf tmux-clean /usr/local/bin/tmuxc

# フォントの既定。素の fontconfig は sans-serif を中国語フェイス (WenQuanYi Zen Hei) へ
# 向けるため、日本語を描くと中国語の字形で写る (#161)。/etc/fonts/local.conf は
# conf.d/51-local.conf 経由で読まれ、これを中国語へ向けている 64 / 65 より「先」になる。
# conf.d/99-*.conf へ置くと <alias><prefer> が効かない。理由と実測は fonts-local.conf の
# 先頭のコメントにある。利用者は ~/.config/fontconfig/fonts.conf (スロット 50) で上書きできる。
COPY --chmod=0644 fonts-local.conf /etc/fonts/local.conf

# フォントのキャッシュを作り直す。上の Playwright の RUN が --with-deps で
# fonts-wqy-zenhei / fonts-ipafont-gothic / fonts-liberation を入れた「後」でなければ、
# 後から入った書体を知らないキャッシュが残る。直後に ~/.cache を消しているため、
# ここで /var/cache/fontconfig を作り直してコンテナ初回起動時の生成を避ける。
RUN sudo fc-cache -f

# entrypoint と dind
COPY --chmod=755 entrypoint.sh /entrypoint.sh
COPY --chmod=755 dind /usr/local/bin/dind
Expand Down
101 changes: 101 additions & 0 deletions containers/base/fonts-local.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<!--
base コンテナの日本語の描画 (devbasex/devbase#161)

このファイルは /etc/fonts/local.conf へ置く。conf.d/ へ置いてはいけない。

/etc/fonts/fonts.conf は <include>conf.d</include> の 1 行しか持たず、conf.d を番号順に
読む。local.conf は conf.d/51-local.conf 経由で読まれる ── つまりスロット 51 で、
sans-serif を WenQuanYi Zen Hei (中国語) へ向けている 64-wqy-zenhei.conf と
65-nonlatin.conf より「先」である。

<prefer> は一致した総称ファミリの直前へ挿入する (prepend) ため、最も早く読まれた
<prefer> が先頭に残る。効くのは順序が後だからではなく、先だからである。

同じ内容を conf.d/99-devbase-fonts.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

<match target="pattern"> の規則 (下の zh-cn / ko) はどのスロットからでも効くが、
<alias><prefer> は 64 / 65 より先に読まれないと効かない。だから 99 では直らない。
規則の種類によって順序への依存が違うため、「99 でも一部は効く」ことを知らないと、
部分的に直ったのを見て置き場所の問題を見落とす。

利用者は ~/.config/fontconfig/fonts.conf (スロット 50、このファイルより先) で
上書きできる。実測で確認済み。
-->
<fontconfig>
<!-- 総称ファミリの先頭を日本語フェイスにする -->
<alias><family>sans-serif</family><prefer><family>Noto Sans CJK JP</family></prefer></alias>
<alias><family>sans</family><prefer><family>Noto Sans CJK JP</family></prefer></alias>
<alias><family>serif</family><prefer><family>Noto Serif CJK JP</family></prefer></alias>
<alias><family>monospace</family><prefer><family>Noto Sans Mono CJK JP</family></prefer></alias>

<!--
未導入の書体を指定されたときの受け皿 (Meiryo / Yu Gothic / MS PGothic /
Zen Kaku Gothic New など)。弱い結合なので実在する指定は妨げない。Arial は
30-metric-aliases.conf (スロット 30、このファイルより先) の強い結合で
Liberation Sans へ向くため、受け皿は末尾に付くだけで結果を変えない。
-->
<match target="pattern">
<edit name="family" mode="append" binding="weak"><string>Noto Sans CJK JP</string></edit>
</match>

<!--
中国語・韓国語を明示したときは、その言語の、しかも同じ様式のフェイスを保つ。
総称ファミリを名指しした指定にだけ効かせる (<test name="family">)。

この test を省くと、Arial:lang=zh-cn のような欧文の指定まで CJK のフェイスへ
奪われる (binding="strong" の前置は名指しの書体よりも強いため)。実測済み。
様式も崩れる ── serif:lang=zh-cn が sans のフェイスになり、monospace:lang=zh-cn が
等幅でなくなる。だから総称ファミリ 4 つ × 言語 2 つ = 8 つを並べる。

zh-cn は OS 既定の 65-nonlatin.conf と 70-fonts-noto-cjk.conf が正しく扱うので
規則なしでも合うが、ko は合わない (上の <alias> が JP を先頭にするため、韓国語が
日本語の字形になる)。ko だけ書くと非対称で後から読めないため、8 つで対称にする。
-->
<match target="pattern">
<test name="family"><string>sans-serif</string></test>
<test name="lang" compare="contains"><string>zh-cn</string></test>
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK SC</string></edit>
</match>
<match target="pattern">
<test name="family"><string>sans</string></test>
<test name="lang" compare="contains"><string>zh-cn</string></test>
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK SC</string></edit>
</match>
<match target="pattern">
<test name="family"><string>serif</string></test>
<test name="lang" compare="contains"><string>zh-cn</string></test>
<edit name="family" mode="prepend" binding="strong"><string>Noto Serif CJK SC</string></edit>
</match>
<match target="pattern">
<test name="family"><string>monospace</string></test>
<test name="lang" compare="contains"><string>zh-cn</string></test>
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans Mono CJK SC</string></edit>
</match>

<match target="pattern">
<test name="family"><string>sans-serif</string></test>
<test name="lang" compare="contains"><string>ko</string></test>
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK KR</string></edit>
</match>
<match target="pattern">
<test name="family"><string>sans</string></test>
<test name="lang" compare="contains"><string>ko</string></test>
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans CJK KR</string></edit>
</match>
<match target="pattern">
<test name="family"><string>serif</string></test>
<test name="lang" compare="contains"><string>ko</string></test>
<edit name="family" mode="prepend" binding="strong"><string>Noto Serif CJK KR</string></edit>
</match>
<match target="pattern">
<test name="family"><string>monospace</string></test>
<test name="lang" compare="contains"><string>ko</string></test>
<edit name="family" mode="prepend" binding="strong"><string>Noto Sans Mono CJK KR</string></edit>
</match>
</fontconfig>
42 changes: 41 additions & 1 deletion docs/user/container-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ graph TD

| イメージ | ベース | 主な内容 | 用途 |
|---------|-------|---------|------|
| **base** | Ubuntu 26.04 | Docker CLI、Python 3 | 最小限の開発環境 |
| **base** | Ubuntu 26.04 | Docker CLI、Python 3、日本語フォント、PDF / OOXML の道具 | 最小限の開発環境 |
| **general** | base | AWS CLI、gcloud、Terraform、Node.js 20、AI CLI | 汎用開発環境 |
| **php** | general | PHP 8.5、Composer、MySQL Shell | PHP 8.5 系 開発 |
| **php85** | general | PHP 8.5、Composer、MySQL Shell | PHP 8.5 系 開発 |
Expand All @@ -372,6 +372,46 @@ graph TD
| **go** | base | Go 開発環境 | Go 開発 |
| **snapshot** | Ubuntu 26.04 | zstd のみ(約 80MB) | スナップショット専用 |

### 文字の描画と、文書を扱う道具(base 以降)

base イメージは、文字を描くときの既定を**日本語**にしています。総称ファミリ(`sans-serif` /
`sans` / `serif` / `monospace`)と、イメージに無い書体名(`Meiryo` / `Yu Gothic` /
`MS PGothic` / `Noto Sans JP` など)は、いずれも Noto CJK の **JP** フェイスへ解決されます。
fontconfig は Chromium / Playwright のスクリーンショット、PDF の生成、画像の生成がすべて
参照するため、日本語を含むページを撮っても日本語の字形で写ります。

| 指定 | 解決先 |
|------|--------|
| `sans-serif` / `sans` | Noto Sans CJK JP |
| `serif` | Noto Serif CJK JP |
| `monospace` | Noto Sans Mono CJK JP |
| `Arial` / `Times New Roman` / `Courier New` | Liberation Sans / Serif / Mono(metric 互換) |
| `Calibri` / `Cambria` | Carlito / Caladea(metric 互換) |
| `sans-serif:lang=zh-cn` など、言語を明示した指定 | その言語の、同じ様式のフェイス(SC / KR) |

> **言語を明示しない中国語は、日本語の字形で描かれます。** `lang` を伴わない `sans-serif` は
> どちらかの言語を選ばざるをえないためで、意図した振る舞いです。中国語・韓国語で描きたい
> ときは `lang=zh-cn` / `lang=ko` を明示するか、書体を名指ししてください。

設定は `/etc/fonts/local.conf`(`containers/base/fonts-local.conf`)にあります。個人の設定
`~/.config/fontconfig/fonts.conf` はこれより先に読まれるため、コンテナの中で上書きできます。

文書を扱う道具も base に入っています。

| 道具 | 用途 |
|------|------|
| `pdftoppm` / `pdftocairo` | PDF を画像(PNG / JPEG / SVG)にする |
| `pdfinfo` / `pdffonts` | PDF のページ数・寸法・埋め込みフォントを調べる |
| `python3 -c "import PIL"` | 画像の読み書き・変換(Pillow) |
| `python3 -c "import lxml, defusedxml"` | OOXML(.docx / .xlsx / .pptx)を壊さずに読み書きする |

> **LibreOffice と `pip` は入っていません。** LibreOffice は展開 372〜459MB で base の規律に
> 見合わないため入れていません。Python パッケージが要るときは `uv` / `uvx` を使ってください。

> **これらは `devbase build base --no-cache` で base を建て直すと反映されます。**
> `devbase up` だけでは反映されません。派生イメージ(`general` など)を使っている
> プロジェクトは、その派生イメージも建て直してください。

### AI CLI エイリアス

general イメージ以降のコンテナ内では、以下の AI CLI ツールがエイリアスとして利用可能です。
Expand Down
Loading