ブログ記事に図解を足したいと思いつつ、画像を作ってアップして貼るという一連の手作業が面倒で、ずっと後回しにしていました。
記事を書かせているAIに、図解まで作らせて、そのままWordPressへ挿入させられないでしょうか。
結論から言うと、AIが書いたSVGをブラウザのcanvasでPNG化するという経路で、生成から挿入までの自動化が実現できました。
前提として、運営中のWordPressはWAFがブラウザ以外の通信を弾く構成のため、投稿まわりの自動化はすべて対象サイトを開いたブラウザの同一オリジンfetchで行っています。
今回はその延長線上に、図解画像の生成・アップロード・本文挿入を追加した記録です。
前提(最小限)
まず、環境の説明を少しだけしておきます。
このブログはレンタルサーバ上のWordPressで、WAFがブラウザ以外からのアクセスを弾く構成です。
そのため日頃から、下書き投稿や本文更新はREST APIを同一オリジンのfetchで叩く形に統一してきました。
一方で気になっていたのが、本文中に画像が1枚も無い記事の多さ。
構成やフローの説明は、文章だけではどうしても伝わりにくいものです。
そこで、記事の図解づくりから挿入までをAIに任せる仕組みを作ることにしました。
ブラウザのcanvasを画像変換器にする

仕組みの中核は、SVGからcanvas経由でPNGへという変換経路です。
なぜSVGか
AIの出力は、突き詰めればテキストしかありません。
しかし、SVGはXMLで書けるベクター画像なので、テキストを書ける道具なら図が描けることになります。
図形の座標や配色をコードとして扱えるため、サイトのアクセント色で統一するといった調整もテキスト編集だけで済むのが利点です。
もう一つ決めたのが、図中の数値や用語は記事本文にある事実だけを使うという捏造防止のルール。
図は本文より目を引くぶん、事実に無い情報が混ざったときのダメージも大きくなります。
PNG化の実装
SVGからPNGへの変換は、ブラウザの中だけで完結できるのがポイントです。
流れとしては、SVG文字列をBlobにしてblob URLを作り、Imageに読み込ませてcanvasへ描画し、toBlobでPNGを取り出します。
中核部を抜き出すと、以下のようなイメージです。
const img = new Image();
img.src = URL.createObjectURL(new Blob([svg], {type: 'image/svg+xml;charset=utf-8'}));
await new Promise(ok => img.onload = ok);
const cv = document.createElement('canvas');
cv.width = 1920; cv.height = 1080;
const cx = cv.getContext('2d');
cx.fillStyle = '#fff'; cx.fillRect(0, 0, 1920, 1080);
cx.drawImage(img, 0, 0, 1920, 1080);
const blob = await new Promise(ok => cv.toBlob(ok, 'image/png'));
await fetch(`${location.origin}/wp-json/wp/v2/media`, { method: 'POST', credentials: 'include',
headers: {'X-WP-Nonce': nonce, 'Content-Disposition': 'attachment; filename="fig1.png"'},
body: blob });
// 省略: alt_text の設定と本文への挿入
注意点は、外部参照やforeignObjectを含むSVGがcanvasを汚染してしまうこと。
汚染されたcanvasはtoBlobを拒否するため、図形とテキストだけの自己完結したSVGにしておく必要があります。
もう一つ、SVGのfont-familyが実行環境のフォントで解決される点も見落としがちです。
実際、検証用のサンドボックスと実機のChromeでは描画フォントが異なり字面が変わったため、見た目の確認は本番と同じ環境側で行うようにしました。
データを外へ運ばない設計
実装で一番効いたのが、画像データを境界の外へ運ばないという設計判断です。
使っているブラウザ自動化ツールは、実行結果の戻り値が約1KBで切り詰められ、大きなデータはそもそも受け取れません。
PNGをbase64で持ち出して別経路からアップロードする案は、この制約により最初から成立しないと分かりました。
そこで発想を変え、生成からメディアへのPOSTまでを全部ブラウザの中で済ませる構成にしています。
アップロードは /wp-json/wp/v2/media へblobを直接POSTし、アップ後に代替テキストも設定しました。
本文への挿入は content.raw の文字列操作で、対象H2の閉じコメント直後にwp:imageブロックを差し込みます。
このとき本文だけを送信し、公開状態のフィールドには一切触れないことで、公開・下書きの状態を誤って変えない保険を掛けました。
メディアや投稿のエンドポイント仕様はWordPress REST API Handbookにまとまっています。
公開物に入れる前の二重ゲート
公開中の記事を機械で書き換える以上、一番怖いのは壊れた状態に気づかないことです。
そこで、アップロード前と書き込み後に一つずつ検査を挟みました。
アップ前のゲートは、生成した図のスクリーンショットを目視で検品し、文字化けやトーフ、枠からのはみ出し、本文と食い違う記述が無いかを確認するというものです。
書き込み後のゲートでは読み戻し照合を行い、文字数の完全一致・画像ブロックの存在・装飾ショートコード4色の開閉数一致を機械検査しました。
この二段構えで、公開中の3記事に1920×1080のPNGを計6枚挿入し、公開ページでの表示まで確認できています。
あわせて運用ルールにしたのが、図解は文章だけでは伝わりにくい構成・フロー・比較の説明に限り、該当が無ければ0枚が正解という基準。
図を入れること自体が目的化すると、ただの装飾が増えて、記事はかえって読みにくくなってしまうものです。
最後に
今回の学びを一般化すると、制約の強い境界では、データを外へ運ぶ設計を捨て、生成から書き込みまでを境界の内側で完結させるという一言に尽きます。
WAFや戻り値の上限は、一見すると自動化の邪魔者に見えました。
しかし、境界の内側にあるブラウザには、canvasという立派な画像処理環境が最初から備わっています。
データを運ぶ経路を探すより、処理のほうを境界の内側へ持ち込む発想に切り替えたことで、構成はむしろ単純になりました。
サーバに触れない環境でも、画像の生成から挿入までここまで自動化できるという一つの実例。
以上です。










コメントを残す