「テーマフォントが原因」というところまでは、探せば辿り着ける。だが実際にファイルの中で何が参照し合っているのかを見た人は少ない。.xlsxはただのZIPなので、覗くのに5分もかからない。手元の適当なブックで試すなら、拡張子を.zipに変えるかunzipコマンドを叩くだけでいい。
$ unzip -l sample.xlsx
Length Date Time Name
--------- ---------- ----- ----
1234 2026-07-01 09:00 xl/theme/theme1.xml
5678 2026-07-01 09:00 xl/styles.xml
...
2345 2026-07-01 09:00 xl/worksheets/sheet1.xml
犯人はxl/theme/theme1.xmlにいる。中をcatすると、こんな塊が出てくる。
<a:fontScheme name="Office">
<a:majorFont>
<a:latin typeface="Aptos Display"/>
<a:font script="Jpan" typeface="游ゴシック Light"/>
</a:majorFont>
<a:minorFont>
<a:latin typeface="Aptos"/>
<a:font script="Jpan" typeface="游ゴシック"/>
</a:minorFont>
</a:fontScheme>
ここで面白いのは、<a:latin>と<a:font script="Jpan">が別枠だという点だ。「テーマの本文フォント」はひとつの値ではなく、スクリプト(文字体系)ごとに引き当て先が分かれたテーブルになっている。だからMicrosoftが既定フォントをCalibriからAptosに切り替えたと言っても、Latin側の値が変わっただけで、Jpan側の游ゴシックはそのまま据え置かれた環境が大半だ。日本語ユーザーの手元で「文字が変わった」体感が薄かった人がいるなら、理由はこれで説明がつく。英数字だけがAptosに寄っていて、日本語の見た目はずっと游ゴシックのままだったということ。
セル側の参照はxl/styles.xmlの<fonts>コレクションを見るとわかる。
<font>
<sz val="11"/>
<color theme="1"/>
<name val="游ゴシック"/>
<family val="2"/>
<scheme val="minor"/>
</font>
name val="游ゴシック"は一見固定指定に見えるが、<scheme val="minor"/>が付いている限りExcelはこちらを優先してtheme1.xmlを動的参照する。name側の値は、スキームを解釈できない古いリーダー向けのフォールバックのスナップショットに過ぎない。だからExcelのフォント一覧で見分けたい人は、名前の括弧の有無だけでなく「保存された瞬間の値」と「実際に解決される値」が原理的にズレうると理解しておくと、ハマったときの当たりが早い。
もうひとつ、グラフやテキストボックスを含むブックだとxl/drawings/配下にDrawingMLが混ざる。ここでは+mn-lt(minor latin)や+mj-lt(major latin)というショートハンドがそのままtypeface属性に書かれる。セル側のscheme属性と表現は違うが、指している先はtheme1.xmlの同じテーブルだ。グラフだけフォントが変な動きをするときは大抵ここを疑うといい。
実務上ここが効くのは「うちのファイルとあなたのファイルで表示が違う」という水掛け論になったときだ。ブック全体をバイナリでdiffしても意味はない。ZIPの圧縮結果はメタデータ次第で揺れるし、そもそも中身はバラバラのXMLの集合体でしかない。見るべきは該当パーツだけを取り出した差分だ。
$ diff <(unzip -p a.xlsx xl/theme/theme1.xml) <(unzip -p b.xlsx xl/theme/theme1.xml)
これで一発で「テーマそのものが別物」なのか「テーマは同じでセル側のフォント指定だけ違う」のかが切り分けられる。サポート対応でセルを一つずつ見比べるより、file単位でこの2行を打つ方が早い。
共有フォルダに配布しているテンプレートが複数世代あって、どれが「本家」か分からなくなっている職場なら、フォルダ全体を機械的に棚卸しするのも難しくない。
import zipfile, xml.etree.ElementTree as ET, glob
ns = {"a": "http://schemas.openxmlformats.org/drawingml/2006/main"}
for path in glob.glob("*.xlsx"):
with zipfile.ZipFile(path) as z:
xml = z.read("xl/theme/theme1.xml")
root = ET.fromstring(xml)
minor = root.find(".//a:minorFont/a:latin", ns).get("typeface")
print(path, minor)
一つずつExcelで開いてフォントメニューを確認して回るより、これで一覧を吐かせたほうが、どのテンプレートが游ゴシック前提でどれがAptos前提なのか一目でわかる。ドリフトの発生源、つまり「最初にズレた1個」を名指しできるのが強い。
結局のところ、セルを選び直して固定フォントを当てる対処は、その1ファイルへのその場しのぎでしかない。組織として再発を止めたいなら、直すべきはtheme1.xmlを内包しているテンプレート側であって、末端の個々のブックではない。原因を体感で語るフェーズは終わっていて、あとはファイルを開けば済む話だった。
参考: Excelのフォントが勝手に変わって困っている人へ。原因と対策を全部解説(note) / ECMA-376 Office Open XML File Formats