Inkscape CLI × PowerShellでサムネ画像を爆速量産!並列処理による自動生成テクニック

Inkscape CLI × PowerShellでサムネ画像を爆速量産!並列処理による自動生成テクニック HowTo
  1. Inkscapeという選択肢
  2. IllustratorとInkscapeの比較
    1. 価格とライセンス
    2. 自動化の思想
    3. Illustratorの強み
    4. Inkscapeの強み
    5. 弱点と棲み分け
  3. SVGネイティブの強み
    1. SVGネイティブの利点を整理すると:
  4. CLI運用の意義と実践シナリオ
    1. アイキャッチ・サムネ量産
    2. 教材やスライドのバリエーション
    3. ロゴやブランド素材の差し替え
    4. CI/CDでのチェック用レンダリング
    5. なぜCLIなのか(ミニまとめ)
  5. 日本語対応の工夫 ― 文字化けを防ぐために
    1. NFKC正規化で全角/半角の統一
    2. 濁点・半濁点のプレコンポーズ
    3. フォント依存を排除する
    4. 記号の揺れを抑える
    5. 基礎知識とAI活用
  6. 実装コード― “サムネ製造工場”を最短で動かす
    1. 何を作るのか
    2. なぜこの構成なのか
    3. 入出力の定義(この章の想定)
      1. 前提(チェックリスト)
      2. まずは動かす(コマンドだけ)
      3. 失敗しないテンプレ(最小例)
        1. template_safe.svg (コードを保存)
        2. bg.png
        3. keywords.json(どちらのフォーマットでもOK)
      4. 日本語で事故らない小技(要旨)
      5. 実測とおすすめ設定
      6. よくあるつまずき(即解決)
      7. 仕上げの一手
  7. 実装コード
    1. PowerShell版(単発・順次処理)
    2. PowerShell版(並列・爆速・計測付き)
  8. Powershell 7 並列化 Tips + 実測レポート
    1. 並列化Tips
    2. 実測(core i7 8700/1600×900・text-to-path/100枚)
    3. CLIオプション速習(現場で困らない最小セット)
    4. 実行例(最小)
  9. 応用編
    1. 応用A:背景を“コードで”自動生成(SVGだけで完結)
    2. 応用B:長いキーワードの“自動縮小”
    3. 応用C:pngquantで軽量化(30〜60%減)
    4. 応用D:CI/CDに組み込む(GitHub Actions例)
  10. おしまいに — 小さな工場を回しはじめよう
  11. 付録

Inkscapeという選択肢

デザインの世界は長らくIllustrator一強。
しかし、いまや“脱Adobe”の流れが本格化しています。

まず、大きな要因が Adobe製品の値上げ です。IllustratorやPhotoshopを単体で契約しても月額が数千円、コンプリートプランともなれば年間10万円を超える。プロ用途なら経費として割り切れても、ブログやYouTubeのアイキャッチ作成、ちょっとしたサムネイル制作など──「日常的な用途」にまで同じコストをかけるのは非合理です。

こうしたニーズを捉えてもう一つの潮流、Canvaの台頭 があります。
豊富なテンプレートをベースに即席でイメージを作り出せるCanvaは、まさに「ワンショットで映えるSNS画像」の強い味方。ノンデザイナーでも直感的に扱える一方で、作れるのは基本的に「一点もの」であり、大量のバリエーションを機械的に生産するのには向きません。

つまり今の構図はこうです。

  • Illustrator=職人の道具(一点物の最高品質、だが高コスト)
  • Canva=ワンショットイメージ(SNS即席向き、ただし量産は不得意)
  • Inkscape=無料で工場化(SVGネイティブ、CLIで大量生産に強い)

筆者自身もこの課題に直面しました。記事や動画を量産する中で必要になるのは「何百枚ものアイキャッチ」や「タイトル違いのバナー画像」。手で作っていては時間が足りませんし、Illustratorに頼り続ければ固定費は膨らむ一方です。


そこで浮上したのが Inkscape(インクスケープ)です。

オープンソースソフトとして古くから存在し、SVGをネイティブに扱えるベクターエディタ。
以前は「イラレ互換の代用品」という立ち位置でしたが、いまやCLI(コマンドラインインターフェース)機能が充実し、「工場的な自動生成」に最適な道具」として再評価されています。

実際に筆者はここ数ヶ月、アイキャッチ作成にInkscapeを導入しました。
するとどうでしょう──GUIでポチポチ操作するよりも、CLIでテンプレートとJSONを流し込むだけで数百枚の画像が一気に出力される。
その効率の良さに、「もうイラレはFireflyと印刷用途以外には不要では?」と感じるほどでした。

もちろん、Illustratorには独自の強みがあります。
特に最近注目を集めている Firefly連携──AIから直接ベクトルイラストを生成できるのは、現時点ではAdobeだけが提供できるオンリーワンの価値です。
印刷業務におけるCMYKや特色の正確な扱いも、依然としてIllustratorが王者です。

しかし Webや動画向けの「大量生成」用途 に限れば、Inkscape+CLIのほうが圧倒的に効率的。
「匠の一枚絵」はIllustrator、「数百枚のサムネ工場」はInkscape。
この棲み分けこそが、2025年の現場における合理的な選択肢と言えるでしょう。

では、Inkscapeが“工場化”に強い理由──そのカギを握るのがSVGネイティブとCLIです。

IllustratorとInkscapeの比較

IllustratorとInkscapeは、同じ「ベクター画像編集ソフト」でありながら、思想や立ち位置が大きく異なります。
ここでは 価格・自動化・強み・弱み の観点で両者を比較し、特に「自動化」という切り口に注目してみましょう。


価格とライセンス

  • Illustrator:サブスクリプション制。単体契約でも月額数千円、コンプリートプランは年間10万円超。商用で安定運用できるものの、固定費の圧力は無視できません。
  • Inkscape:完全無料のオープンソース。個人利用から商用までライセンスフリー。ソースコード公開、改造も可能。

単純な「導入ハードル」という点では、Inkscapeに軍配が上がります。


自動化の思想

Illustratorの自動化とInkscapeの自動化は、まさに「文化の違い」が表れています。

  • Illustrator
    • アクション:GUI操作を記録・再生する仕組み。繰り返し作業には強いが、条件分岐や外部データ連携には弱い。
    • ExtendScript/JavaScript:より柔軟に自動化可能。ただし学習コストが高く、環境依存もある。
    • 結論:「人間の操作を効率化」する方向に特化。
  • Inkscape
    • CLI(コマンドラインインターフェース)inkscape input.svg --export-type=png --export-filename=out.png のように、完全にGUIを介さず画像を出力できる。
    • 外部スクリプト(PowerShell、Python、Bash)から呼び出し可能。JSONやCSVを流し込めば「自動工場」が即完成。
    • 結論:「人間を介さず、機械にやらせる」方向に強い。

Illustratorのアクションは「匠の技を短縮化」するもの、InkscapeのCLIは「工場の生産ラインを構築」するもの──この対比が両者の違いを端的に表しています。


Illustratorの強み

  • Firefly連携:AIから直接ベクトルイラストを生成できるのは唯一無二。ビットマップからの自動トレースでは得られない「最初からパスデータ」の価値は大きい。
  • 印刷対応力:CMYKや特色、プリフライトチェックなど印刷工程での安心感は依然としてIllustrator独占。
  • 業界標準:広告代理店・印刷会社とのデータ互換性はIllustrator前提で回っている。

Inkscapeの強み

  • SVGネイティブ:保存形式がそのまま国際標準のSVG。Git管理・差分管理・自動生成と相性が良い。
  • CLIレンダリング:GUI不要でPNG/JPG/WebPなどに変換。CI/CDやサーバー上でも稼働可能。
  • OSSの自由度:Dockerでの利用、プラグイン開発、ソースコード改造まで可能。

弱点と棲み分け

  • Illustrator → 印刷・出版、クライアントワーク、クリエイティブ表現
  • Inkscape → Web・動画向けの大量生成、テンプレート処理、教育・実験用途

つまり、Illustratorは「匠の工房」、Inkscapeは「自動化工場」と言えるでしょう。


次章では、この比較の核心となる 「SVGネイティブ」という強み をさらに掘り下げます。
実際のSVGコードを例にとりながら、なぜInkscapeが量産用途に強いのかを見ていきましょう。

SVGネイティブの強み

Illustratorの保存形式が「.ai」であるのに対し、Inkscapeの保存形式は そのまま国際標準のSVG(Scalable Vector Graphics) です。
つまり、画像の正体はただのテキストファイル。テキストエディタで開けば、中身が丸見えです。

たとえば「紫の背景に白文字でHello SVG」と書かれたバナーを作りたいとしましょう。
IllustratorならGUIで操作して.aiファイルを保存することになりますが、Inkscapeなら完成品はたった数行のSVGコードです。

Hello SVG

これはSVGの中身をそのまま表示しているだけです。SVG対応環境ならそのまま絵になります。

このSVG画像のコード

<svg xmlns="http://www.w3.org/2000/svg" width="400" height="200">
  <rect width="400" height="200" fill="#AAF"/>
  <text x="200" y="110" font-size="48" text-anchor="middle" fill="#fff">
    Hello SVG
  </text>
</svg>1

ポイントは「ただのテキスト」だということ。
ここにあるのは <rect> (背景の四角形)と <text> (文字列)の指示だけ。
「文字色を変えたい」「文言を差し替えたい」と思ったら、数値や文字列を直接書き換えれば即反映されます。


SVGネイティブの利点を整理すると:

  • Git管理に最適:テキストだから差分(diff)がそのまま読める
  • 劣化ゼロ:何度保存しても画質が落ちない
  • 自動処理に強い:JSONやCSVから値を流し込むのが簡単
  • 環境を選ばない:ブラウザで即表示できる

Illustratorの「.ai」が閉じたフォーマットであるのに対し、SVGは開かれた標準です。
だからこそInkscapeは 「自動生成」との相性が抜群。CLIから処理を流すときの柔軟性はIllustratorを大きく上回ります。


次章では、この「テキストとしての強み」を実際の 自動化シナリオ に落とし込みます。
サムネイルやアイキャッチを JSON → SVGテンプレ → PNG という流れで一気に生成する仕組みを紹介しましょう。

CLI運用の意義と実践シナリオ

Inkscapeのいちばんの武器は CLI(コマンドライン)
数枚を丁寧に仕上げるならGUIでも十分ですが、「100枚を同じ品質で一気に」が必要になった瞬間、CLIの出番です。

仕組みは単純

  • テンプレートSVG … レイアウトや装飾を固定
  • 差し込みデータ(JSON/CSV) … タイトルやキーワードだけを入れ替え
  • これを スクリプト → Inkscape CLI に流すだけで、何百枚でも無人で量産できます。
    Illustratorのアクションが「人の手を早くする道具」だとすれば、InkscapeのCLIは そもそも人を貼り付けないための仕組みです。


アイキャッチ・サムネ量産

ブログやYouTubeの“毎回違う”アイキャッチ。
見た目の骨格は共通でよいなら、テンプレ+差し込みテキストで工場化が最適解。
夜のうちに回しておけば、朝にはまとまった枚数がそろいます。

  • テンプレSVG:背景・余白・影・縁取りを固定
  • 差し込み:記事タイトル/用語/番号 など

教材やスライドのバリエーション

教育現場の定番、「同じ問題で数値だけ違う」版の量産。
CSVの列を変えるだけで、10パターンも100パターンも一気に展開できます。
図表や注記の位置がテンプレで固定されるため、レイアウト崩れも起きにくいのが利点。


ロゴやブランド素材の差し替え

同じレイアウトで、クライアントごとにロゴ/配色だけを変えたいケース。
SVGの <image> やカラー変数に差し込み、一括レンダリング
CI/CDに載せれば、ブランチを切る→プレビュー画像が自動で添付 という運用も現実的です。


CI/CDでのチェック用レンダリング

コードレビューで 「SVGの差分は読めるけど、見た目がわからない」 問題。
Inkscape CLIで PRごとにPNGを自動生成し、プレビューとして添付すれば、
デザイナーもエンジニアも同じ“見た目”を確認できます。レビュー効率が一段上がります


なぜCLIなのか(ミニまとめ)

  • 無人・再現性:同じ入力から同じ出力。手作業のムラが消える
  • スケール:データを増やすだけで枚数が伸びる
  • 統合:スクリプト化すれば、スケジューラやCI/CDにそのまま載せられる

次章では、この自動化を日本語環境で確実に回すための「文字化け対策」を解説します。
NFKC正規化や濁点・半濁点の落とし穴──実戦でつまずきがちなポイントを、最初から潰していきましょう。

日本語対応の工夫 ― 文字化けを防ぐために

Inkscape CLIを自動化の現場に投入するとき、最大の壁が 日本語処理 です。
欧文フォントだけならまず問題は出ませんが、日本語では「文字コードの揺れ」「濁点・半濁点の扱い」「フォント依存」が頻発します。


NFKC正規化で全角/半角の統一

  • 「ABC」と「ABC」は見た目が同じでも内部コードは別物。
  • 「ー」(長音)と「-」(ハイフン)も混在しがち。
  • スクリプト内で NormalizationForm.FormKC を使い、全角・半角を正規化しておくと事故が減ります。

濁点・半濁点のプレコンポーズ

「パ」「ピ」のように分解された文字が混ざると、レンダリング環境によっては「は?」という表示崩れになります。
スクリプトで以下のように置換しておくのが安全策です:

# パ → パ
$s = $s -replace "(\u30CF)\u309A",[string][char]0x30D1
# ピ → ピ
$s = $s -replace "(\u30D2)\u309A",[string][char]0x30D4

これで「パ行」は常にプレコンポーズされた形で保存されます。


フォント依存を排除する

日本語フォントは環境差が激しく、「自分のPCでは表示されるのに他人の環境では豆腐(□)」という事態もよくあります。
Inkscape CLIには --export-text-to-path オプションがあり、テキストをアウトライン化してフォント依存を根こそぎ排除できます。
これを常用すれば、生成結果がどこでも再現可能になります。


記号の揺れを抑える

  • 中黒「・」とピリオド「.」
  • ダッシュ「–」とハイフン「-」
  • これらも正規化処理で揃えておくと安心です。

基礎知識とAI活用

活用のためには、PythonやPowerShellなどスクリプト言語の基礎知識が欠かせません。
とはいえ「正規化ってどう書くの?」「この置換コード正しい?」という疑問はつきものです。
そんなときは ChatGPTなどのAIに相談すれば、きっと現場に合わせたサンプルコードを提示してくれます
「自動化工場を作る人」と「工場の設計図を描くAI」という役割分担を意識すれば、専門家でなくても十分に回せます。


次章では、いよいよ実際に 「テンプレSVG+keywords.json → PNG出力」 までの最小サンプルを示します。
ここを読めば「自動化工場の入り口」が誰でも再現できるようになります。

実装コード― “サムネ製造工場”を最短で動かす

この章では、1つのテンプレートSVG+背景画像+用語リスト(JSON)を材料に、
InkscapeのCLIを PowerShell だけ で回して、キーワードごとのユニークなPNGサムネを一括生成します。

何を作るのか

  • 単発(順次)版:確実に1枚ずつ書き出すベースライン。計測(Total/Avg/Throughput)付き。
  • 並列(爆速)版:PowerShell 7の ForEach-Object -Parallel を使い、体感3–4倍のスループットを狙う決定版。

なぜこの構成なのか

  • 再現性と自動化:GUI操作を記録するより、コードで確定してCIにも載せられる。
  • Windows現場との相性:制作者の多くがWindows環境。PowerShell 7なら追加ランタイム不要で並列が簡単。
  • 日本語の安定出力--export-text-to-path でフォント依存を排除。正規化で文字化けを予防。
  • スケール前提:100語でも1000語でも、テンプレ+データ差し替えでそのまま増やせる。

入出力の定義(この章の想定)

  • 入力
    • template_safe.svg{{BG}} / {{TITLE}} / {{KEYWORD}} を含むテンプレ
    • bg.png … 背景画像(スクリプトが file:///… に置換)
    • keywords.json["配列","アルゴリズム",…] または [{"keyword":"配列"},…]
  • 出力
    • thumbs/配列.png のように 語ごとのPNG を量産(既定 1600×900)

コードが苦手でも大丈夫。必要な最小スクリプトはすべて記載し、
迷いがちな日本語まわり(正規化・濁点・記号)も“事故らない初期値”で用意しています。
わからない箇所は ChatGPT などに断片的に相談しても進められるよう、関数は小さく独立させています。

前提(チェックリスト)

  • PowerShell 7 以上($PSVersionTable で確認)
  • Inkscape インストール&CLIパス(例:C:\Program Files\Inkscape\bin\inkscape.exe
  • 作業フォルダに以下の3つ
    • template_safe.svg<image href="{{BG}}"> / {{TITLE}} / {{KEYWORD}} を含む)
    • bg.png(背景)
    • keywords.json["配列","アルゴリズム","再帰"] など)

まずは動かす(コマンドだけ)

# シングル(順次・計測付き)
pwsh -File .\make_thumbs_single.ps1 `
  -TemplateSvg template_safe.svg -Keywords keywords.json -Bg bg.png -OutDir thumbs

# 並列(爆速・計測付き:Jobsは自動でコア数-2)
pwsh -File .\make_thumbs_parallel.ps1 `
  -TemplateSvg template_safe.svg -Keywords keywords.json -Bg bg.png -OutDir thumbs -Jobs 8

成功すると thumbs/配列.png などが出力され、最後に Total / Avg / Thruput を表示。

失敗しないテンプレ(最小例)

template_safe.svg (コードを保存)
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1600" height="900" viewBox="0 0 1600 900"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:space="preserve">

<!-- 背景画像:スクリプトで {{BG}} を file:///…(または data:URI)に置換 -->
<image x="0" y="0" width="1600" height="900"
href="{{BG}}" xlink:href="{{BG}}"
preserveAspectRatio="xMidYMid slice" />

<!-- タイトル(上部中央) -->
<text id="title" x="800" y="96" text-anchor="middle"
font-family="Noto Sans JP, 'Yu Gothic UI', Meiryo, 'Hiragino Kaku Gothic ProN', sans-serif"
font-size="52" font-weight="700" fill="#fff" letter-spacing=".02em">
{{TITLE}}
</text>

<!-- キーワード(中央・自動折返し:下層は黒縁取り) -->
<flowRoot id="keyword-stroke" xml:space="preserve"
style="font-family:Noto Sans JP, 'Yu Gothic UI', Meiryo, 'Hiragino Kaku Gothic ProN', sans-serif;
font-size:96px; line-height:125%; text-align:center;
fill:none; stroke:#000; stroke-width:6; stroke-linejoin:round;">
<flowRegion><rect x="130" y="430" width="1340" height="320"/></flowRegion>
<flowPara>{{KEYWORD}}</flowPara>
</flowRoot>

<!-- キーワード(上層・白文字) -->
<flowRoot id="keyword" xml:space="preserve"
style="font-family:Noto Sans JP, 'Yu Gothic UI', Meiryo, 'Hiragino Kaku Gothic ProN', sans-serif;
font-size:96px; line-height:125%; text-align:center; fill:#fff;">
<flowRegion><rect x="130" y="430" width="1340" height="320"/></flowRegion>
<flowPara>{{KEYWORD}}</flowPara>
</flowRoot>
</svg>
bg.png
keywords.json(どちらのフォーマットでもOK)
["配列","アルゴリズム","再帰"]
[{"keyword":"配列"},{"keyword":"アルゴリズム"},{"keyword":"再帰"}]

日本語で事故らない小技(要旨)

  • NFKC正規化、濁点/半濁点のプレ合成、記号統一
  • --export-text-to-path でフォント依存ゼロに
  • 背景は {{BG}} → file:///C:/.../bg.png に置換(相対パス禁止)

実測とおすすめ設定

  • Single:02:26 / 100枚 → 40.8 img/min
  • Parallel(8):00:39 / 100枚 → 152.6 img/min(約 3.7×
  • 目安:Jobs = ProcessorCount - 2、UIが重ければ 6〜8
  • プロセス優先度を下げると操作が楽 [System.Diagnostics.Process]::GetCurrentProcess().PriorityClass='BelowNormal'

よくあるつまずき(即解決)

  • 背景が出ない → テンプレに <image href="{{BG}}"> があるか/file:///… に置換できているか
  • 並列が動かない → PS7+ で実行しているか(Windows PowerShell 5.1 では不可)
  • Using エラー → -Parallel 内では $using:変数 をローカルに退避してから使う
  • 文字化け → --export-text-to-path を付けたか/UTF-8(BOMなし)で保存したか

仕上げの一手

  • pngquant --quality 70-90 で 30–60% 軽量化
  • 長文キーワードは {{FS}} をテンプレに仕込み、スクリプトでフォントサイズ調整
  • CI/CD に組み込み、PRごとにPNGプレビューを自動生成

実装コード

PowerShell版(単発・順次処理)

PowerShell7x対応。Windowsならフォント周りの相性もよく、タスクスケジューラに載せやすいのが利点。

# make_thumbs_single.ps1  (PowerShell 7+ 推奨)
param(
  [string]$TemplateSvg = "template_safe.svg",
  [string]$Keywords    = "keywords.json",
  [string]$OutDir      = "thumbs",
  [string]$Title       = "春日部つむぎのIT用語講座",
  [string]$Bg          = "bg.png",
  [string]$Inkscape    = "C:\Program Files\Inkscape\bin\inkscape.exe"
)

function Normalize-Jp([string]$s){
  if ([string]::IsNullOrEmpty($s)) { return $s }
  $s = $s.Normalize([Text.NormalizationForm]::FormKC)
  $s = $s -replace "[‐-–—−]", "-"   # ダッシュ統一
  $s = $s -replace "[·∙•・]", "・"  # 中黒統一
  $s = $s.Normalize([Text.NormalizationForm]::FormC)
  return $s
}
function Read-Keywords([string]$path){
  $raw = Get-Content -Raw -Encoding UTF8 $path | ConvertFrom-Json
  $list = @()
  foreach($x in $raw){ if($x -is [string]){ $list += $x } elseif($x.keyword){ $list += $x.keyword } }
  return $list
}
function Write-Utf8NoBom([string]$Path,[string]$Content){
  [IO.File]::WriteAllText($Path, $Content, (New-Object Text.UTF8Encoding($false)))
}

# 実行パス安定化
$here        = Split-Path -Parent $PSCommandPath
$TemplateSvg = Join-Path $here $TemplateSvg
$Keywords    = Join-Path $here $Keywords
$OutDir      = Join-Path $here $OutDir
$Bg          = Join-Path $here $Bg

# 前提チェック
if (!(Test-Path $Inkscape))    { throw "Inkscape not found: $Inkscape" }
if (!(Test-Path $TemplateSvg)) { throw "Template not found: $TemplateSvg" }
if (!(Test-Path $Keywords))    { throw "Keywords not found: $Keywords" }
if (!(Test-Path $OutDir))      { New-Item -ItemType Directory -Force $OutDir | Out-Null }

# テンプレ読込 & 背景を絶対URIで埋め込み
$tpl   = Get-Content -Raw -Encoding UTF8 $TemplateSvg
$bgUri = "file:///" + ((Resolve-Path $Bg).Path -replace '\\','/')
$tpl   = $tpl.Replace("{{BG}}", $bgUri)
# ※ テンプレ側に <image href="{{BG}}" .../> が必要

$TitleN = Normalize-Jp $Title
$kws    = Read-Keywords $Keywords

# 計測開始
$sw = [System.Diagnostics.Stopwatch]::StartNew()

foreach($kw in $kws){
  $kwN = Normalize-Jp $kw
  $svg = $tpl.Replace("{{TITLE}}",$TitleN).Replace("{{KEYWORD}}",$kwN)

  $tmp  = Join-Path $env:TEMP ("thumb_"+[guid]::NewGuid().ToString("N")+".svg")
  Write-Utf8NoBom $tmp $svg

  $safe = ($kwN -replace '[\\/:*?"<>|]', '_')
  $out  = Join-Path $OutDir ("$safe.png")

  & "$Inkscape" $tmp --export-type=png --export-filename="$out" `
     --export-area-page --export-width=1600 --export-text-to-path | Out-Null

  Remove-Item $tmp -ErrorAction SilentlyContinue
  Write-Host "made: $out"
}

# 計測終了
$sw.Stop()
$cnt   = $kws.Count
$avgMs = [math]::Round($sw.Elapsed.TotalMilliseconds / [math]::Max($cnt,1))
$tpm   = [math]::Round($cnt / [math]::Max($sw.Elapsed.TotalMinutes,1e-9),1)
Write-Host ("Total: {0:mm\:ss\.fff}  Items: {1}  Avg: {2} ms/item  Thruput: {3} img/min" -f $sw.Elapsed, $cnt, $avgMs, $tpm)

PowerShell版(並列・爆速・計測付き)

PowerShell 7 の ForEach-Object -Parallel を使って同時実行。
目安:コア数−2-ThrottleLimit に。

# make_thumbs_parallel.ps1  (PowerShell 7+ 必須)
param(
  [string]$TemplateSvg = "template_safe.svg",
  [string]$Keywords    = "keywords.json",
  [string]$OutDir      = "thumbs",
  [string]$Title       = "春日部つむぎのIT用語講座",
  [string]$Bg          = "bg.png",
  [int]$Jobs           = ([math]::Max(4,[Environment]::ProcessorCount - 2)),
  [string]$Inkscape    = "C:\Program Files\Inkscape\bin\inkscape.exe"
)

# UI軽量化(任意)
[System.Diagnostics.Process]::GetCurrentProcess().PriorityClass = 'BelowNormal'

function Normalize-Jp([string]$s){
  if ([string]::IsNullOrEmpty($s)) { return $s }
  $s = $s.Normalize([Text.NormalizationForm]::FormKC)
  $s = $s -replace "[‐-–—−]", "-"
  $s = $s -replace "[·∙•・]", "・"
  $s = $s.Normalize([Text.NormalizationForm]::FormC)
  return $s
}
function Read-Keywords([string]$path){
  $raw = Get-Content -Raw -Encoding UTF8 $path | ConvertFrom-Json
  $list = @()
  foreach($x in $raw){ if($x -is [string]){ $list += $x } elseif($x.keyword){ $list += $x.keyword } }
  return $list
}
function Write-Utf8NoBom([string]$Path,[string]$Content){
  [IO.File]::WriteAllText($Path, $Content, (New-Object Text.UTF8Encoding($false)))
}

# パス安定化
$here        = Split-Path -Parent $PSCommandPath
$TemplateSvg = Join-Path $here $TemplateSvg
$Keywords    = Join-Path $here $Keywords
$OutDir      = Join-Path $here $OutDir
$Bg          = Join-Path $here $Bg

# 前提
if (!(Test-Path $Inkscape))    { throw "Inkscape not found: $Inkscape" }
if (!(Test-Path $TemplateSvg)) { throw "Template not found: $TemplateSvg" }
if (!(Test-Path $Keywords))    { throw "Keywords not found: $Keywords" }
if (!(Test-Path $OutDir))      { New-Item -ItemType Directory -Force $OutDir | Out-Null }

# テンプレ読み込み & 背景絶対URI化
$tpl   = Get-Content -Raw -Encoding UTF8 $TemplateSvg
$bgUri = "file:///" + ((Resolve-Path $Bg).Path -replace '\\','/')
$tpl   = $tpl.Replace("{{BG}}", $bgUri)

$TitleN = Normalize-Jp $Title
$kws    = (Read-Keywords $Keywords) | ForEach-Object { Normalize-Jp $_ }
$cnt    = $kws.Count

# 計測開始
$sw = [System.Diagnostics.Stopwatch]::StartNew()

$kws | ForEach-Object -Parallel {
    # Using は“変数のみ” → 先にローカルへ退避
    $tplLocal   = $using:tpl
    $titleLocal = $using:TitleN
    $outDirLoc  = $using:OutDir
    $inkLoc     = $using:Inkscape

    # 軽いランダム待機でI/Oスパイク緩和(任意)
    Start-Sleep -Milliseconds (Get-Random -Minimum 0 -Maximum 80)

    function Write-Utf8NoBom([string]$Path,[string]$Content){
      [IO.File]::WriteAllText($Path, $Content, (New-Object Text.UTF8Encoding($false)))
    }

    $kw  = $_
    $svg = $tplLocal.Replace("{{TITLE}}",$titleLocal).Replace("{{KEYWORD}}",$kw)

    $tmp  = Join-Path $env:TEMP ("thumb_"+[guid]::NewGuid().ToString("N")+".svg")
    Write-Utf8NoBom $tmp $svg

    $safe = ($kw -replace '[\\/:*?"<>|]', '_')
    $out  = Join-Path $outDirLoc ("$safe.png")

    try{
      & $inkLoc $tmp --export-type=png `
        --export-filename=$out --export-area-page --export-width=1600 `
        --export-text-to-path | Out-Null
      Write-Host "made: $out (PID=$PID)"
    } catch {
      Write-Warning "failed: $kw - $($_.Exception.Message)"
    } finally {
      Remove-Item $tmp -ErrorAction SilentlyContinue
    }
} -ThrottleLimit $Jobs

# 計測終了
$sw.Stop()
$avgMs = [math]::Round($sw.Elapsed.TotalMilliseconds / [math]::Max($cnt,1))
$tpm   = [math]::Round($cnt / [math]::Max($sw.Elapsed.TotalMinutes,1e-9),1)
Write-Host ("Total: {0:mm\:ss\.fff}  Items: {1}  Avg: {2} ms/item  Thruput: {3} img/min  (Jobs={4})" -f $sw.Elapsed, $cnt, $avgMs, $tpm, $Jobs)

ワンポイント

  • i7-8700(12スレッド)なら -ThrottleLimit 6〜8 が体感ちょうど良い
  • Inkscapeはプロセス並列にそこそこ強いが、SSDとCPU温度を見ながら調整
  • 失敗は catch で握ってログに回す(工場は止めない)

Powershell 7 並列化 Tips + 実測レポート

並列化Tips

  • PS7必須ForEach-Object -Parallel は PowerShell 7+ 専用。$PSVersionTable で確認。
  • Usingのコツ$using: は“変数のみ”。$using:tpl.Replace(...) のようなメソッド呼び出しはNG → いったんローカル変数へ。
  • ジョブ数(ThrottleLimit):目安は [Environment]::ProcessorCount - 2。I/OやUIの具合で 6/8/10/12 を計測して“膝”を決める。
  • UIカクつき対策:プロセス優先度を下げる [System.Diagnostics.Process]::GetCurrentProcess().PriorityClass='BelowNormal' ついでに 0〜80ms のランダム待機でI/Oスパイクをならすと◎。
  • 相対パスの地雷:一時SVGを %TEMP% に出すため、背景画像は file:/// の絶対URIに置換(上のコード済み)。
  • フォント依存を排除--export-text-to-path を常用。
  • 圧縮(任意)pngquant --quality 70-90 を生成直後に回すと 30–60%軽量化。
  • ウォームアップ:最初の1枚はフォントキャッシュで重くなることあり。ダミー1枚を先に流すと安定。

実測(core i7 8700/1600×900・text-to-path/100枚)

モードTotalItemsAvgスループットJobs
Single02:26.9491001469 ms40.8 img/min1
Multi00:39.320100393 ms152.6 img/min8

加速率 ≈ 3.74×(8ジョブに対して効率 ~47%)。起動・I/Oオーバーヘッドを考えると優秀な伸び。

PS7恐るべしですね。こんなに簡単に並列コンピューティングの恩恵に預かれるとは..。それも、シングルスレッドアプリを無理やりパラレルで駆動するところに浪漫を感じませんか?

この手法、他にもたくさん応用できそうで楽しめそうです。

CLIオプション速習(現場で困らない最小セット)

Inkscape 1.2+ 系の書式で統一。基本は “入力SVG → 出力PNG/JPG/WebP”

  • --export-type=png|jpg|webp|svg|pdf
    出力形式。量産はだいたい png 固定でOK。
  • --export-filename <path>
    出力先パス。必ずフルパス or 既存ディレクトリに。
  • --export-area-page
    ページ全体を出力(テンプレはこれで安定)。
  • --export-width <px> / --export-height <px>
    どちらか指定でアスペクト維持。サムネは --export-width=1600 を推奨。
  • --export-dpi <number>
    ラスター系のDPI指定。Web前提なら未指定でOK。
  • --export-text-to-path
    日本語は必須。フォント依存を排除し再現性を確保。
  • --export-background-opacity=0
    透過が要るとき(今回のテンプレは不要)。
  • --export-overwrite
    既存ファイル上書き。バッチ時に便利。

実行例(最小)

inkscape template_safe.svg \
  --export-type=png \
  --export-filename=thumbs/配列.png \
  --export-area-page --export-width=1600 --export-text-to-path

落とし穴メモ

  • 背景画像は {{BG}} → file:///…/bg.png に置換(相対パスはテンポラリ出力で死にます)
  • 古いInkscapeの -o / -w の書式とは混ぜない(1.2+の新書式で統一)

応用編

応用A:背景を“コードで”自動生成(SVGだけで完結)

外部PNGなしでも背景は作れます。ノイズ×色相は軽くて映える定番。

※掲載の都合上、1600×900をそれぞれ半分にしています

応用B:長いキーワードの“自動縮小”

flowRootを使わない標準SVG版なら、フォントサイズをコードで調整

# Powershell
# 
# 文字数に応じてフォントサイズを決める関数(お好みで調整)
function Fit-FontSize([string]$s, [int]$max=150, [int]$min=72){
  $n = [Math]::Max(1, ($s.Length))
  $scale = if($n -le 6){1.0} elseif($n -le 10){0.85} elseif($n -le 14){0.72} else{0.60}
  [int]([Math]::Max($min, $max * $scale))
}
# 使い方: $fs = Fit-FontSize $kw ; $tpl.Replace("{{FS}}",$fs)

テンプレ側は font-size="{{FS}}" にしておけばOK。

応用C:pngquantで軽量化(30〜60%減)

# 生成直後に圧縮(失敗時は元を残す)
& ".\pngquant.exe" --force --skip-if-larger --quality=70-90 --ext .png $out | Out-Null

YouTubeサムネやブログ用は、1MB→数百KBまで落ちることが多いです。

応用D:CI/CDに組み込む(GitHub Actions例)

PRを作ったら SVG→PNG を自動生成し、成果物として添付。
(UbuntuにInkscapeを入れて実行します)

yaml

name: render-thumbs
on: [pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Inkscape
        run: sudo apt-get update && sudo apt-get install -y inkscape
      - name: Render PNGs
        run: |
          mkdir -p thumbs
          inkscape template_safe.svg \
            --export-type=png \
            --export-filename=thumbs/sample.png \
            --export-area-page --export-width=1600 --export-text-to-path
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: thumbs
          path: thumbs/

おしまいに — 小さな工場を回しはじめよう

ここまで読んでくださって、本当におつかれさまでした。
テンプレートを1枚、用語をいくつか、そしてInkscapeのCLI。
それだけで、あなたのPCの中に “サムネ工場” が立ち上がりました。

Illustratorが職人の作業台だとしたら、InkscapeのCLIは ベルトコンベア
PowerShellはその スイッチ です。
ボタンを押すたびに、同じクオリティの画像が次々と出来上がる。
──それって、ちょっとワクワクしません?

難しいところはAIに甘えましょう。
正規化の書き方、正規表現の一行、CIのYAML……。
「ここだけ手伝って」で十分。設計はAI、指揮はあなた。
それで立派に回ります。

明日からやることはシンプルです。

  • テンプレを自分色に:フォント・余白・影を少し整える
  • 語を足すkeywords.json に思いつく限り追加

すると気づくはず。
気合いと根性で1枚ずつ作っていた頃とはもう違う、百枚/分クラスのスループット工場が手元にあることを。
予定表の空き時間に、思い立ったキーワードを流し込むだけで、明日の素材がそろう。
その余白で、もっと面白いことを考えられる。

最後に、合言葉だけ置いておきます。

匠の一枚はIllustrator。量産の工場はInkscape。
そして、工場はコードで回す。

さあ、あなたの工場を動かそう。
次は、背景を生成するレシピを増やす?
それとも、ffmpegで 動画工場 に拡張する?
どっちでも大丈夫。ベルトはもう、あなたの指先にあります。

付録

Inkscape の公式サイトです。

Inkscape - Draw Freely. | Inkscape
Inkscape is professional vector graphics software which runs on Linux, macOS and Windows desktop computers.