画像生成モデルはフォントエンジンを持っていない。文字は「意味のある記号」ではなく“絵”として一緒に描かれる(→ 本編②「なぜ崩れるのか」)。だから「このフォントファイルで組版する」という概念がそもそも無い。加えて、API のリクエストにフォントを指定する場所が無い(model・prompt・size・quality・background…で、font は無い)。指定しようがないのだ。
実際にやってみる。プロンプトに「フォントは必ず Noto Sans JP を使うこと」と名指ししても、出てくるのは素のゴシックで、ただ「ゴシック体で」と書いたときと見分けがつかない。3回試して毎回同じで、名指しによる差は出なかった(そもそも API にフォントを渡す引数が無い以上、効きようがない)。
つまり「フォント名を書く」という発想自体が、画像生成では空振りする。効くのは“見た目の系統”を表す言葉だけだ。
一方で、書体の系統を表す言葉はちゃんと届く。同じ「森のパン工房」を、ゴシック体/明朝体/丸ゴシック体/筆文字で頼み分けると、gpt-image-2 は見てわかるレベルで描き分けた。
明朝は横画が細く縦画が太いコントラストのある字面に、丸ゴは角が丸く、筆文字は毛筆になった。系統としてはおおむね機能している。ただし限界がはっきりある。
使いどころは「雰囲気だけ出ればよく、文字の正確さに責任を負わない装飾」まで。ブランドフォントや、文字が正確である必要のある場面では style語では足りない。
狙ったフォントをそのまま入れたいなら、モデルに文字を描かせないのが答えだ。本編③「実装で打てる対策」の M7 として挙げた手で、要は① 文字を置く場所を空けた“文字なし背景”を生成 → ② コードで実フォントを重畳する。
モデルに頼んだ“明朝”と、合成した本物の明朝を並べてみる。左はモデルの「明朝体で」(明朝寄りで、これはこれで悪くない。ただしウロコや太さは run ごとにブレる)、右はヒラギノ明朝を合成したもの(毎回このウロコちょうど)。差は“品質”というより“狙って固定できるかどうか”にある。
同じ背景に書体だけ差し替えれば、選んだフォントちょうどが手に入る。ゴシック(Noto Sans JP)・明朝・丸ゴ・極太、どれでも思いどおりだ。
しかもこの経路は決定論的だ。同じ背景・同じ文字列・同じフォントで合成すれば、出力は毎回バイト単位で同一(md5 一致を確認)。崩れる余地が原理的に無い。背景生成は安い low のままでよく、コストも上がらない。
from PIL import Image, ImageDraw, ImageFont
bg = Image.open("bg_textless.png").convert("RGBA") # 文字なし背景(gpt-imageで生成)
W, H = bg.size
d = ImageDraw.Draw(bg)
font = ImageFont.truetype("NotoSansJP-Bold.otf", 200) # ← ここで“狙ったフォント”を固定
box = d.textbbox((0, 0), "森のパン工房", font=font)
x = (W - (box[2]-box[0]))/2 - box[0]
y = (H - (box[3]-box[1]))/2 - box[1]
d.text((x, y), "森のパン工房", font=font,
fill=(38,28,18), stroke_width=10, stroke_fill=(255,255,255)) # 白フチで可読性
bg.convert("RGB").save("out.png")
Web 系なら HTML+Canvas / SVG → PNG でも同じこと。要は「描画はコード、生成は背景だけ」に分ければ、フォントは完全にこちらの管理下に入る。
合成は確実だが、出せるのはそのフォントが持っているグリフだけ。フォントに字が無ければ豆腐(□)になる。人名・社名のレア漢字でこれを踏む。
試しに 髙﨑德𠮷鷗邊 を4フォントで描くと、補助平面(4バイト)の 𠮷(つちよし・U+20BB7) が ヒラギノ角ゴシック W8 では豆腐になった。Noto Sans JP・ヒラギノ明朝・丸ゴには入っている。「システムフォントだから安心」とは限らない。
対策はシンプル。被覆の広い和文フォント(Noto Sans JP/源ノ角ゴシック)を使う、そしてレアな人名漢字は描画して目視確認する。異体字(髙・﨑・𠮷 など)が正式表記の固有名詞は誤字が致命的なので、ここは必ず一度レンダリングして確かめる(→ 周辺編 異体字・旧字体)。IVS(異体字セレクタ)が要るケースは、対応フォント+セレクタ付き文字列で固定する。
「サンプル画像を渡して“この書体で”と言えば?」もよく聞く。最初は1枚だけ試して「prompt-onlyより寄るっぽい」と書いたが、1試行で言い切るのは怖い。そこで明朝・丸ゴ・筆の3系統 ×「言葉だけ(prompt-only)」と「参考画像を渡す」の2条件 × 各5回=30枚を生成し、どっちの条件で作ったかを伏せたまま、独立な判定者2名で書体を分類して固め直した(明朝・丸ゴの参考画像は実フォント、筆だけは和文の筆フォントが手元に無いのでAIで描いた見本を使った)。
結果は、事前の「参考画像のほうが寄る」という見立てを裏切った。系統ごとにバラバラで、しかも丸ゴでは逆効果だった。
丸ゴは逆効果。 言葉だけ(「丸ゴシック体で」)は5回とも丸ゴになったのに、丸ゴの参考画像を渡すと出力はむしろ角ゴ化し、丸ゴと判定されたのは5回中1回だけ(残りは角ゴ)。「見せれば寄る」どころか、言葉だけより悪化した。
明朝は差が出なかった。 言葉だけも参考画像も、明朝と判定されたのはどちらも5回中4回(各1回だけ角ゴに振れた)。参考画像を渡しても上積みは無い。最初の1試行で「寄った」と思えたのは、たまたま当たりを引いただけだった。
筆は両方とも完璧。 言葉だけ・参考画像とも5回すべて筆。元から特徴が強くて崩れにくい系統では、どちらでも問題ない。
なお文字の正確さ(「森のパン工房」の6字)は30枚すべてで保たれた(独立判定60件すべて「崩れなし」)。少なくともこの強経路では、参考画像を渡しても文字は崩れない——崩れないが、書体は寄らない、という話だ。
丸ゴだけ逆効果だったが、参考画像の条件は edit API+絵のある背景、言葉だけは generations+無地で作っていた。これだと「参考画像 vs 言葉」に「edit/背景の有無」が混ざる。そこで丸ゴで (言葉だけ / 参考画像) ×(無地の背景 / 絵のある背景) の4条件を各5回ずつ、同じく条件を伏せて判定し、切り分けた。
はっきりした。逆効果は「参考画像」と「絵のある背景」が揃ったときだけ起きる相互作用だった。どちらか片方を外すと崩れない——無地+参考画像も、絵の背景+言葉だけも、各5/5で丸ゴ。念のため「参考画像+絵の背景」だけ独立にもう一度5枚回しても、やはり角ゴ化が再現した(2回の実行を合わせて10枚中8枚が角ゴ)。だから単なる当たり外れではない。推測だが、背景の絵と参考画像の両方を同時に辻褄合わせしようとすると、モデルは丸みを捨てて無難な角ゴに寄る——そんな挙動に見える。
しかもこれは実用上タチが悪い。画像を生成する時点でふつう背景には絵がある。つまり「絵のある背景に、参考画像で書体を寄せたい」といういちばんやりたい使い方でこそ逆効果が出る。無地でなら参考画像が効くとはいえ、無地ならそもそも合成すれば済む話だ。
まとめると、「参考画像を見せれば狙った書体に寄る」は当てにならない。元から寄る系統(筆)では言葉だけでも足り、明朝は言葉だけと差がなく、丸ゴは(実用的な絵の背景では)むしろ逆効果になりうる。正確な書体が要るなら、ここでも結論は合成に戻る。なお、この丸ゴの逆効果や文字崩れが別モデルでどう変わるかは gpt-image-2 vs gpt-image-1.5 のモデル比較編 で検証した(逆効果は gpt-image-2 に強く出る癖で、gpt-image-1.5 では弱い)。
画像生成AIに「特定のフォントで」と頼むのは、構造的に空振りする——モデルはフォントを持たず、API にも指定する場所が無い。効くのは系統を表す style語までで、それも“っぽい何か”止まり。狙ったフォントそのものが要るなら、文字なし背景を生成してコードで実フォントを合成するのが唯一の確実な道だ。決定論で毎回同じ、コストも上がらない。唯一気をつけるのはグリフ被覆——被覆の広いフォントを選び、レア漢字は描画確認する。これは 異体字 や 実装対策 で繰り返し出てきた「確定テキストは合成」の、具体的なやり方そのものだ。