PerfectVector
著者: Irene Kim

隠れたパスを残さずSVGを完全に切り抜く

実際の交差演算で塗りつぶしたSVGパスを切り抜き、色分けと開口部を保持します。クリッピングだけでは隠れる形状が残っていないか、保存済みファイルを確認します。

3 分で読める
目次

塗りつぶしたSVGアートワークを完全に切り抜くには、対象の各形状と切り抜き領域の共通部分を求め、結果の形状だけを書き出します。編集可能なマスターは別に保存してください。viewBoxを小さくしたり、クリッピングパスを使ったりすると、見える範囲は変わっても、元の外側のパスがファイルに残る場合があります。

今回の例では、独自に作成した4つの不透明で閉じた多角形オブジェクトと、長方形の切り抜き領域を使います。1つのオブジェクトには実際の開口部があります。塗りと描画順を保つため各オブジェクトを個別に切り抜き、SVGとして保存して、その実際のファイルを開き直して確認します。これは対象を限定した形状処理のワークフローであり、すべてのSVG効果を切り抜いたり、検証済みのカッター用ファイルを作成したりする汎用的な方法ではありません。

絵柄を隠すのか、削除するのかを決める

クリッピングは、後で切り抜き位置を調整したい場合に便利です。MDNのクリッピングのリファレンスでは、レンダリング結果と要素の元の形状を区別しています。クリッピングの関係を解除すると、元の絵柄が再び見えることがあります。

完全な切り抜きでは、受け渡す形状そのものを変更します。次の作業者が範囲内に収めた形状自体を必要とする場合に使い、切り抜き前の元データは別のマスターとして保持してください。

操作変わるものその操作だけでは分からないこと
ビューポートを変更表示するドキュメントの領域外側のパスが削除されたか
クリップを追加描画する部分元の形状が変わったか
塗りつぶした各オブジェクトの共通部分を求める結果の塗りの境界任意の線、効果、受け取り側アプリへの対応
完全に不要なオブジェクトを削除ファイル内でのそのオブジェクトの有無境界をまたぐ別のオブジェクトの切り抜き

見えない要素をすべて削除することは、別の作業です。クリップ用の形状、マスクのリソース、参照されるシンボルは、直接描画されていなくても必要な場合があります。クリップパスとマスクのガイドではその描画上の関係を、クリップ座標のガイドでは座標系を説明しています。

識別できる閉じた塗りから始める

編集可能な元データ、定義した切り抜き領域、保存したSVGを調べる方法が必要です。この例はブラウザーでPaper.js 0.12.18を実行します。線のない、単色で完全に不透明な多角形の塗りを扱います。演算対象は任意のインポート済みグループではなく、PathまたはCompoundPathオブジェクトです。

元データには、長方形の開口部がある青緑の枠、オレンジの三角形、紫の菱形、切り抜き領域の完全に外にある小さな黄色のオブジェクトが含まれます。切り抜く長方形は(60, 40)から始まり、160 × 120のSVGユーザー単位です。これは作成した座標であり、実際の印刷寸法ではありません。

枠の開口部は複合パスに属します。ループで処理するためだけに、その輪郭を無関係な塗りつぶしオブジェクトに分解しないでください。SVGの穴のガイドでは、白い塗りと実際の透明な開口部の動作が異なる理由を説明しています。

この元のテストデータをsource.svgとして保存します。

<svg xmlns="http://www.w3.org/2000/svg"
     width="300" height="220" viewBox="0 0 300 220">
  <path id="frame" fill="#39786E" fill-rule="evenodd"
        d="M20 20H160V180H20Z M85 70V100H115V70Z"/>
  <path id="triangle" fill="#DE9252"
        d="M150 0L270 110L150 200Z"/>
  <path id="leaf" fill="#80618E"
        d="M180 130L240 150L180 190L120 150Z"/>
  <path id="outside" fill="#D4B35E" d="M250 10H280V30H250Z"/>
</svg>

各オブジェクトの共通部分を求め、結果だけを残す

交差演算では、オブジェクトと切り抜き領域が共有する面積を残します。Paper.jsのintersect()の説明では、共通部分を含む新しいパス項目を返すと説明しています。ここではtrace: trueによって両方の演算対象を面として扱います。このオプションはブーリアン形状演算に関するもので、ラスター画像をベクターに変換するものではありません。insert: falseを使い、読み込んだマスターも切り抜き用の補助オブジェクトも、書き出すシーンに自動で入らないようにします。

ローカルで再現するには、空の作業フォルダーに指定バージョンのライブラリをインストールします。

npm install --save-exact paper@0.12.18

以下をindex.htmlとして、source.svgと後述のcrop.jsの横に保存します。

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Permanent SVG crop</title>
<canvas id="work" width="300" height="220" hidden></canvas>
<p id="status">Running crop…</p>
<pre id="svg-output"></pre>
<pre id="receipt"></pre>
<script src="node_modules/paper/dist/paper-full.min.js"></script>
<script src="crop.js"></script>
</html>

以下をcrop.jsとして保存します。ローカルHTTPサーバーでフォルダーを配信し、index.htmlを開いてください。スクリプトは同じフォルダーのSVGファイルを取得します。

(async () => {
  paper.setup(document.getElementById('work'));
 
  const source = await (await fetch('source.svg')).text();
  const imported = paper.project.importSVG(source, {
    insert: false,
    expandShapes: true
  });
  const crop = new paper.Path.Rectangle({
    rectangle: new paper.Rectangle(60, 40, 160, 120),
    insert: false
  });
  const delivery = new paper.Group({ insert: false });
  const rows = [];
 
  for (const name of ['frame', 'triangle', 'leaf', 'outside']) {
    const operand = imported.getItem({ name });
    if (!(operand instanceof paper.Path || operand instanceof paper.CompoundPath)) {
      throw new Error('Expected filled path: ' + name);
    }
 
    const result = operand.intersect(crop, { insert: false, trace: true });
    if (Math.abs(result.area) < 1e-8) {
      rows.push({ name, removed: true });
      result.remove();
      continue;
    }
 
    result.fillColor = operand.fillColor.clone();
    result.fillRule = operand.fillRule;
    result.strokeColor = null;
    result.opacity = 1;
    result.name = 'cropped-' + name;
    delivery.addChild(result);
    rows.push({
      name,
      result: result.className,
      fill: result.fillColor.toCSS(true),
      area: result.area
    });
  }
 
  delivery.translate(new paper.Point(-60, -40));
  paper.project.activeLayer.addChild(delivery);
  const output = paper.project.exportSVG({
    asString: true,
    precision: 6,
    bounds: new paper.Rectangle(0, 0, 160, 120)
  });
 
  document.getElementById('svg-output').textContent = output;
  document.getElementById('receipt').textContent = JSON.stringify({
    paper: paper.version,
    rows,
    export_precision: 6,
    viewport: [0, 0, 160, 120]
  }, null, 2);
  document.getElementById('status').textContent = 'Intersection/export executed. Save the exact visible SVG output as cropped.svg and open that saved file independently.';
})().catch(e => {
  document.getElementById('status').textContent = 'FAIL: ' + e.message;
});

明示した名前のリストは、元データの描画順に従っています。残す結果はそれぞれ固有の単色の塗りを受け取り、その順序で追加されます。最初にすべての絵柄を1つのシルエットに結合すると、保持したい別々の色領域が失われます。Paper.jsのブーリアン演算のガイドでは、結合と差分という別の作業を扱っています。

完全に外側にある黄色のオブジェクトは共通部分が空になるため、省かれます。小さな面積のしきい値は、このテストデータのための保護処理にすぎず、任意の絵柄から細部を削除してよいという意味ではありません。

残す形状を(-60, -40)だけ平行移動し、ゼロから始まる160 × 120のビューポートに書き出します。この移動で変わるのは、出力の座標原点です。不要な外側の形状を削除するのは交差演算であり、原点やビューポートを変えるだけでは削除できません。Paper.jsのプロジェクト書き出しのリファレンスでは、ここで使う明示的な範囲と数値精度を説明しています。

実際のSVGを保存し、開き直して確認する

表示されたSVGマークアップを正確にcropped.svgへコピーします。プログラムのメモリ内プレビューだけに頼らず、保存したファイルを独立して開いてください。今回のテストでは、別のページで保存ファイルを読み込み、新しいPaper.jsスコープにインポートし、ブラウザーのSVG画像レンダラーで表示しました。

元の閉じた塗り形状、クリッピングだけのプレビュー、完全に切り抜いて独立して開き直したSVGと形状チェックを比較する実際のブラウザー表示
独自に作成した多角形の絵柄と、実際の保存ファイルの検証です。クリッピングでは元のオブジェクトが残り、交差演算で書き出したファイルには残す形状だけが含まれます。任意のSVGやカッターとの互換性を示すものではありません。

検証では、ファイル構造、形状、見た目を分けています。

  • 書き出したSVGには結果のパス要素が3つあり、枠が開口部を保持するため、閉じた輪郭は4つあります。
  • この条件を管理した出力には、切り抜き用補助オブジェクト、元のオブジェクトID、クリッピングパス、マスク、ラスター画像、効果要素はありません。
  • 開き直した輪郭はすべて閉じており、直線の線分で構成されます。頂点はすべて出力範囲の0…160×0…120内にあり、許容誤差は0.00001ユーザー単位です。
  • 意図した開口部は塗り形状に含まれず、レンダリングしたファイルでも透明です。確認した重なりの点では、オレンジの領域が引き続き青緑より上に表示されます。
  • ブラウザーのレンダリング比較では、形状の調査とは別に、クリッピングだけのプレビューと切り抜いたファイルを同じサイズで比較します。

この長方形の範囲と直線で構成したテストデータでは、すべての頂点と閉じる辺を確認することで、保存された輪郭が範囲内に収まると分かります。ハンドルのある曲線や凹形の切り抜き範囲では、別の包含確認が必要です。いくつかの点を抽出するだけでは、同じ結果は証明できません。

画像比較では640 × 480のラスター画像を使い、いずれかのチャンネルの差が1を越えるピクセルについて、変更ピクセル0.5%までという許容値を明示しています。保存ファイルのレンダリングでは、307,200ピクセル中77ピクセル、約0.0251%に差があり、その基準に合格しました。ピクセル単位で同一ではなく、個々のチャンネルの最大差は255でした。これは見た目の比較の許容値であり、形状の許容誤差ではありません。端付近のアンチエイリアスと、実際に外側のパスが残っていることは、別の問題です。

この演算は、切り抜き領域の外側の形状を削除します。残した色付きオブジェクト間のすべての重なりを削除するわけではありません。内部の重なりは描画順のまま残り、検証した点で意図した最上部の色を確認しています。隠れている描画上の重なりをすべて解消することは、別の作業です。

絵柄に別の表現方法が必要なら止める

この例は、開いた中心線、線、曲線の演算対象、グラデーション、ソフトマスク、ラスター画像、フィルター、任意の入れ子になったクリップ群、半透明の重なった描画への対応を示していません。それらの特徴を削除しても見た目が保たれると考えないでください。

線が付いた経路と、その線の輪郭を塗り形状にしたものでは、形状が異なります。見えている線の面積を塗り形状にする必要がある場合は、ブーリアン演算のワークフローを選ぶ前に、適切なエディターで線をアウトライン化したコピーを準備し、確認してください。編集性が重要なら、元の線も残します。ソフトマスクを硬い境界の交差演算に置き換えて、薄れていく端をすべて保持することはできません。

一度だけの編集なら、ベクターエディターのほうが必要なオブジェクトを選択して確認しやすい場合があります。どのツールを選んでも、保存した結果の穴、色、境界を確認してください。次の工程で使う前には、書き出し精度と非常に小さい細部に特に注意が必要です。

必要な場合だけ不足したベクターアートワークを復元する

元データが既存の編集可能なSVGなら、その形状を直接切り抜きます。適切なラスターのクリップアート画像しか残っておらず、編集可能な形状が必要なら、PerfectVectorのクリップアート用ワークフローで先にSVGの候補を作成できます。切り抜く前に、復元した輪郭、開口部、別々の塗りを確認してください。ベクター化はピクセルからパスを作るものであり、希望する切り抜き境界を特定したり、ブーリアン演算の結果を検証したりはしません。今回の元のテストデータは直接作成しており、トレースしていません。

質感が必要で、入稿形式が意図したラスターのトリミングに対応するなら、イラストはラスターのまま使います。ベクターの切り抜きが役立つのは、次の工程で形状が必要なためであり、すべての画像をSVGにすべきだからではありません。

マスターを失わずに切り抜きを完了する

  1. 切り抜き前のマスターを保持し、対象の閉じた塗りオブジェクトを特定します。
  2. それらのオブジェクトと同じ座標系で切り抜き範囲を定義します。
  3. 各オブジェクトの共通部分を求め、検証したスタイルと描画順を維持し、補助オブジェクトと元データを受け渡し用シーンから除外します。
  4. 範囲と精度を意図的に選び、残す形状だけを書き出します。
  5. 保存ファイルを開き直し、実際のパス、開口部、見た目、使う予定の受け取り先をそれぞれ確認します。

保存ファイルに意図した形状が範囲内に含まれ、必要な穴と色の関係が保たれていれば、範囲を限定した切り抜きは完了です。外側の形状が残っている場合は、元のプロジェクト内容を書き出していないか、クリップやビューポートだけを変更していないかを確認してください。開口部が埋まった場合は、さらにオブジェクトを結合する前に、複合パスと塗り規則を確認します。

元の多角形のテストデータについて、保存ファイルを対象とする作業を完了しました。カッターへの読み込みや実物の出力テストは行っておらず、SVGの一般的な互換性を保証するものでもありません。

よくある質問

SVGをクリッピングすると、外側のパスは完全に削除されますか? クリッピングは参照する形状を保持したまま、レンダリングを制限します。形状を完全に切り抜くには、適切なパス演算と、結果だけを含む書き出しが必要です。

先に形状を結合すれば、すべての色を切り抜けますか? 1回の結合で、残したかった個別の塗り領域が失われる場合があります。この例では、単色で塗りつぶした各オブジェクトの共通部分を個別に求め、元の描画順を維持します。

viewBoxを小さくすると、隠れた絵柄は削除されますか? 変わるのはビューポートの対応付けと表示範囲です。それだけで範囲外の元のパスが削除されるわけではありません。プレビューに加えて、保存された形状も確認してください。

結果はすべてのカッティングアプリで使えますか? このブラウザーテストだけでは分かりません。使用先で読み込んだ形状と処理を確認し、適切な制作上の検証を行ってください。

出典

  1. MDN:SVG clipPath — クリッピングは、参照する要素の形状を変えずにレンダリングに作用します。
  2. Paper.js:PathItem — 交差演算の結果と、面に基づくブーリアン演算のオプションを説明しています。
  3. Paper.js:Project — SVGの読み込み、プロジェクト内容、書き出し範囲、数値精度を説明しています。

ビットマップしかないクリップアートからパスが必要ですか?SVGの候補を準備し、穴と塗りを確認してから、別の作業用コピーを切り抜き、受け渡す前に保存した結果を開き直しましょう。

ブログのその他の記事

PerfectVector

編集しやすい
よりきれいなSVGから始めましょう

クレジットカード不要