ダウンロードボタンの追加と
xlink:href問題
(svged開発記)

前回までに、ブラウザ上でSVGコードとプレビューを双方向に編集できる簡易エディタを作りました。

<svg class="eyecatch-svg" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg">
  <!-- 背景 -->
  <rect width="192" height="192" fill="white" rx="32" ry="32"/>
  
  <!-- エディタウィンドウ -->
  <rect x="24" y="40" width="144" height="112" fill="#E0E0E0" rx="8" ry="8"/>
  <rect x="24" y="40" width="144" height="24" fill="#607D8B" rx="8" ry="8"/>
  <rect x="24" y="64" width="144" height="88" fill="white" rx="0" ry="0"/>
  
  <!-- ウィンドウドット -->
  <circle cx="36" cy="52" r="3" fill="#F44336"/>
  <circle cx="48" cy="52" r="3" fill="#FF9800"/>
  <circle cx="60" cy="52" r="3" fill="#4CAF50"/>
  
  <!-- コード行(左側) -->
  <line x1="32" y1="76" x2="80" y2="76" stroke="#9E9E9E" stroke-width="3" stroke-linecap="round"/>
  <line x1="32" y1="88" x2="72" y2="88" stroke="#9E9E9E" stroke-width="3" stroke-linecap="round"/>
  <line x1="32" y1="100" x2="88" y2="100" stroke="#2196F3" stroke-width="3" stroke-linecap="round"/>
  
  <!-- SVG図形プレビュー -->
  <g transform="translate(112, 92)">
    <circle cx="0" cy="0" r="16" fill="none" stroke="#2196F3" stroke-width="3"/>
    <rect x="-8" y="-8" width="16" height="16" fill="#4CAF50" opacity="0.6" rx="2" ry="2"/>
  </g>
  
  <!-- 変換矢印 -->
  <g transform="translate(96, 130)">
    <path d="M -12 0 L 12 0 M 6 -6 L 12 0 L 6 6" fill="none" stroke="#FF9800" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
  </g>
  
  <!-- チェックマーク(正規化完了) -->
  <g transform="translate(150, 136)">
    <circle cx="0" cy="0" r="12" fill="#4CAF50"/>
    <path d="M -4 0 L -1 4 L 5 -4" fill="none" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
  </g>
  
  <!-- Pasteアイコン -->
  <g transform="translate(40, 130)">
    <rect x="-6" y="-8" width="12" height="16" fill="none" stroke="#9C27B0" stroke-width="2" rx="2" ry="2"/>
    <rect x="-3" y="-10" width="6" height="3" fill="#9C27B0" rx="1" ry="1"/>
  </g>
</svg>ダウンロードボタンの追加と<br class="chiilabo-br is-on">xlink:href問題<br class="chiilabo-br is-on">(svged開発記)

何回か使っていると、SVGコードを外部から持ってきて試したり、完成したSVGを保存したりする流れが、思ったよりスムーズではないように感じました。
そこで、新しく貼り付けとダウンロードのボタンを追加しました。

関連記事

1. コード操作を楽にするためのボタン追加

1.1. Pasteボタンを追加しようと思った理由

これまでのエディタでは、SVGコードを貼り付ける場合、テキストエリアをクリックして手動で貼り付ける必要がありました。操作自体は難しくありませんが、何度も繰り返すと地味に手間です。

そこで、「コードをコピー」ボタンの隣に、「コードを貼り付け」ボタンを追加することにしました。ボタンを押すだけで、現在のコードをすべて置き換えて貼り付けられるようにするのが狙いです。

1.2. ダウンロードボタンも同時に追加

もう一つ追加したのが、Preview側の「ダウンロード」ボタンです。編集結果をSVGファイルとして保存する操作は、このエディタのゴールの一つです。右上に専用のボタンを置くことで、操作の流れが自然になります。

実装としては、Blobを使ってSVG文字列をファイル化し、日付をファイル名にしたシンプルなものです。この時点では、特に問題は感じていませんでした。

2. ダウンロード後に発覚したXMLエラー

ところが、ダウンロードしたSVGをブラウザで開くと、XMLエラーが表示されました。エディタ上では正しく描画されているのに、ファイルとしてはエラーになる。この差に引っかかりを覚えました。

2. ダウンロード後に発覚したXMLエラー

エラーメッセージを読むと、「xlink:href の名前空間が定義されていない」とあります。場所も <use> 要素の行だと分かりました。

2.1. SVGファイルを直接確認する

原因をはっきりさせるため、保存したSVGをテキストエディタで開きました。すると、<use xlink:href="..."> という記述がありました。一方で、<svg> タグには xmlns:xlink がありません。

XMLとして考えると、これは確かに不正です。ただ、ブラウザ上のプレビューでは問題なく表示されていました。ここで、「ブラウザの寛容さ」と「XMLとしての厳密さ」の違いを強く意識しました。

3. xlink:hrefという古い仕様

調べてみると、xlink:href はSVG 1.1で使われていた古い書き方でした。現在のSVG 2では、単に href を使うのが正式です。つまり、今回のエラーは、古い仕様の書き方がそのまま残っていたことが原因でした。

これまでエディタ内で扱っていたSVGは、外部からコピーしたものが多く、その中にSVG 1.1由来の記述が含まれていました。表示だけを確認して満足していたため、ファイルとしての正しさを見落としていたわけです。

3.1. Paste時に正規化する

最終的に選んだのは、Paste時に変換する方法です。外部から入ってくるSVGは、この時点で一度正規化する。その後は、エディタ内部では新しい仕様のSVGだけを扱う。この流れが、一番分かりやすいと感じました。

Pasteボタンの処理で、クリップボードから取得した文字列に対し、xlink:href を href に置換しました。同時に、不要になった xmlns:xlink も削除します。その後、コードを丸ごと置き換えてプレビューを更新します。

この修正で、ダウンロードしたSVGのXMLエラーは完全に解消しました。操作感も改善され、Pasteから編集、ダウンロードまでの流れが一気に滑らかになりました。

4. 振り返り

今回の作業は、UI改善という軽い気持ちで始めました。しかし、途中で仕様の世代差という、少し根の深い問題にぶつかりました。

結果的には、Pasteという「入口」で正規化する設計に落ち着きました。小さな判断ですが、エディタ全体の見通しは良くなったと感じています。実際に使ってみることで初めて見える課題があり、それを一つずつ潰していく過程そのものが、今回の一番の収穫でした。