zudo-paper

IllustratorのSVGをKiCadにインポートする際の3つの問題をPythonで修正する

Author: Takazudo | 作成: 2026/03/17

概要

KiCadでPCB基板を設計する際、Illustratorで作成した形状(外形線やパネルのフレーム形状など)をSVGとしてエクスポートし、KiCadにインポートして使うケースがある。しかし、IllustratorのSVGエクスポートとKiCadのSVGインポートには互換性の問題があり、そのままでは正しく読み込めない。

具体的には以下の3つの問題がある。

  • 穴あき形状(compound path)が壊れる
  • スケールがズレる(72 DPI vs 96 DPI)
  • CSSスタイルが無視される

これらをPythonスクリプトで一括修正するツールを作ったので、そのまとめ。

背景

Illustratorで「穴あき矩形」(外枠+内枠の複合パス)を作り、SVGでエクスポートした。KiCadのPCBエディタでEdge.CutsレイヤーにこのSVGをインポートしたところ、穴の部分が認識されず、スケールもズレた。原因を調べてみたところ、SVGのcompound path(複合パス)の仕組みとKiCadのSVGインポーターの解釈の違いが根本にあった。

問題1: 穴あき形状が壊れる

Illustratorでの見た目

Illustratorでは、穴あき形状はcompound pathとして表現される。外枠の矩形の中に内枠の矩形が「穴」として正しく見えている状態。

Illustratorでのcompound pathの表示。外枠の中に穴が正しく見えている

SVGの構造

IllustratorのSVGエクスポートでは、このcompound pathは1つの<path>要素のd属性に、複数のサブパスとして格納される。

<path d="M0,0v112.4h373.5V0H0ZM352.3,84.5H21.2V27.9h331.1v56.7Z"/>

このd属性には2つのサブパスが含まれている。

  • M0,0v112.4h373.5V0H0Z --- 外枠の矩形
  • M352.3,84.5H21.2V27.9h331.1v56.7Z --- 内枠の矩形(穴)

ブラウザやIllustratorでは、SVGのfill-rule(even-oddやnonzero)に基づいて、内側のパスを「穴」として描画する。重なっている部分が透明になる仕組み。

KiCadでの解釈

KiCadのSVGインポーターはこのfill-ruleを理解しない。compound pathの全サブパスを1つの連続ポリゴンとして解釈してしまう。結果として、外枠の終点と内枠の始点を直線で結んだ「ブリッジ」が生まれ、本来は穴であるべき部分が充填された12頂点ポリゴンになる。

KiCadでの壊れたインポート結果。外枠と内枠がブリッジで結合された奇妙なポリゴン

本来は外枠4頂点+内枠4頂点の2つの独立したポリゴンのはずが、結合されて1つの12頂点ポリゴンになってしまっている。

問題2: スケールがズレる

Illustratorは歴史的に72 DPI(1ポイント = 1ピクセル)で座標を管理している。一方、SVG仕様(CSS)のデフォルトは96 DPI。KiCadはSVGの座標を96 DPI基準で解釈するため、Illustratorから出力されたSVGは約75%のサイズで読み込まれてしまう。

実際に測定してみると、Illustrator上で 131.78 mm の幅の形状が、KiCadにx1スケールでインポートすると 98.822 mm になった。この比率は 131.78 / 98.822 = 1.33350873 で、理論値の 96/72 = 1.33333… と近いがわずかに異なる。スクリプトではこの実測値をデフォルトの補正係数として使っている。

問題3: CSSスタイルが無視される

IllustratorのSVGエクスポートは以下のようなCSS形式でスタイルを定義する。

<defs>
  <style>
    .st0 { fill: #231f20; }
  </style>
</defs>
<path class="st0" d="..."/>

KiCadのSVGインポーターはCSS <style>class属性を処理しないため、パスのスタイル情報が完全に無視される。

修正スクリプト

Pythonスクリプトfix-svg-for-kicad.pyを作成した。標準ライブラリのみ(xml.etree.ElementTreere)で動作し、追加のpipインストールは不要。

やっていることは以下の3つ。

  • compound pathの分割: d属性をZ+Mの境界で分割し、各サブパスを独立した<path>要素に変換
  • スケール補正: 全座標値に1.33350873を乗算(Arc命令のフラグや角度は除く)
  • スタイルクリーンアップ: <defs>/<style>を削除、class属性を削除、fill="none" stroke="black"に置換

使い方

python3 fix-svg-for-kicad.py input.svg output.svg
python3 fix-svg-for-kicad.py input.svg  # → input-fixed.svg に出力
python3 fix-svg-for-kicad.py input.svg --no-scale  # スケール補正なし
python3 fix-svg-for-kicad.py input.svg --scale 1.5  # カスタム倍率

変換前後の比較

変換前のSVG。

<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1"
     viewBox="0 0 1133.9 1133.9">
  <defs>
    <style>
      .st0 { fill: #231f20; }
    </style>
  </defs>
  <path class="st0"
        d="M0,0v112.4h373.5V0H0ZM352.3,84.5H21.2V27.9h331.1v56.7Z"/>
</svg>

変換後のSVG。

<?xml version='1.0' encoding='UTF-8'?>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" version="1.1"
     viewBox="0.0 0.0 1512.0655 1512.0655">
  <path d="M 0.0 0.0 v 149.8864 h 498.0655 V 0.0 H 0.0 Z"
        fill="none" stroke="black" stroke-width="0.1" />
  <path d="M 469.7951 112.6815 H 28.2704 V 37.2049 h 441.5247 v 75.6099 Z"
        fill="none" stroke="black" stroke-width="0.1" />
</svg>

1つの<path>が2つの独立した<path>に分割され、座標もスケール補正されている。<defs>/<style>は削除され、各パスにfill="none" stroke="black"が直接指定されている。

修正後のKiCadインポート

修正後のSVGをKiCadにインポートすると、外枠と内枠が正しく別々のポリゴンとして認識される。

KiCadでの修正後のインポート結果。外枠と内枠が別々のポリゴンとして正しく認識されている

ステータスバーの「Points: 4」表示が、各ポリゴンが独立した4頂点の矩形として認識されていることを示している。

代替手段

Pythonスクリプト以外にも、いくつかの方法がある。

  • Inkscapeでの前処理: Illustratorから出力したSVGをInkscapeで開き、compound pathを手動で分割してから再保存する。GUIで作業できるが、ファイル数が多い場合は手間がかかる
  • DXFエクスポート: IllustratorからDXF形式でエクスポートし、KiCadに読み込む。ただしDXFにはDXFで別の互換性問題がある
  • svg2mod: SVGをKiCadのフットプリントモジュールに変換するツール。用途が合えば便利だが、Edge.Cutsへの単純なインポートとは異なる

今回はファイル数もそこそこあったのと、何度も繰り返す作業だったので、スクリプトで自動化する方向にした。

余談

IllustratorとKiCadのDPIの不一致は、Illustratorが72 DPIで座標を管理しているという歴史的な経緯に由来する。これはIllustratorがPostScriptの1ポイント = 1/72インチという仕様をベースにしているため。SVG仕様のほうはCSSに合わせて96 DPIを採用しているので、ここにギャップが生まれる。

理論値の96/72 = 1.33333…ではなく実測値の1.33350873を使っているのは、Illustratorの内部的な座標丸めなどが影響しているのだろうと考えられる。ただ、大きな基板でなければ理論値でも十分実用的な精度になる。

スクリプトは標準ライブラリのみで動作するので、Python 3が入っていれば追加のインストール無しですぐ使える。compound pathの分割ロジックはd属性のZの後にMが続くパターンで切っているだけなので、シンプルな形状であれば問題なく動く。ベジェ曲線が複雑に絡み合うようなケースでは、Inkscapeのほうが確実かもしれない。

今回のスクリプトとClaude Codeのスキル定義は以下で公開している。

自分が使っているClaude Codeのカスタムコマンドやスキルなどのリソースは claude-resources リポジトリで公開しているので、興味があれば参照してほしい。