Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
14
Help us understand the problem. What is going on with this article?

More than 1 year has passed since last update.

@j8takagi

SVGのimage要素での外部画像ファイル参照をbase64コード(URIスキーマ)に変換する

この記事は、SVG Advent Calendar 2016の6日目の記事です。
また、説明つきの画面キャプチャー図をSVGでつくるの続編です。

はじめに

説明つきの画面キャプチャー図をSVGでつくるで取り上げたようにSVGのimage要素を使って外部の画像ファイルを参照させると、いろいろ便利です。
しかし、外部の画像ファイルを参照させたSVGファイルをHTMLのimgタグなどで参照しようとすると、うまくいきません。画像ファイルが欠落してしまうのです。
とりあえずの対策として、SVGファイルをPNGなどの画像に変換するという方法もあります。
だけど、せっかく世の中の大多数のWebブラウザーでSVGを扱えるようになった時代、できればSVGのままHTMLから使いたいものです。

外部画像ファイル参照をbase64コード(URIスキーマ)に変換する

いろいろ調べると、image要素で参照する画像ファイルをbase64コードに変換してURIスキーマを使えば、HTMLのimgタグなどで参照しても画像ファイルが欠落しないらしいです。

画像ファイルをbase64コードに変換するというのも、調べればいろいろな方法がでてきます。
Rubyを使う場合は、次のプログラムで画像ファイルをbase64コードに変換できます。

次のRubyプログラムファイルを作成します。
なお、library base64はRubyの標準添付ライブラリです。

imgfile2base64.rb
require 'base64'

def imgfile2base64(imgfile)
  Base64.strict_encode64(File.new(imgfile).read)
end

puts imgfile2base64(ARGV[0])

シェルなどから次のように実行することで、base64コードを出力できます。

$ ruby imgfile2base64.rb 01.png

次にSVGファイルで、image要素のxlink:href属性の内容を置換します。
xlink:href属性として記載されているファイル名を削除し、かわりにdata:image/png;base64,を入力します。
image/pngの部分は、参照する画像ファイルの MIMEタイプ
data:image/png;base64,に続き、base64コードをコピーアンドペーストするなどして入力し、保存します(別名での保存を推奨)。

ここまで作業したら、SVGファイルが正常に表示されるかテストしてください。
まずは置換後、もとのファイルと同じようにSVGファイルが表示されていることを確認してください。
確認できたら次に、このSVGファイルをHTMLファイルからimg要素を使って参照してみましょう。
HTML文書中で画像部分も含めてSVGが正常に表示されていれば成功です。

base64コードへの変換を自動化する

image要素のxlink:href属性に記載されているファイル名をbase64コードに置換する作業は、自動化したいですね。

ライブラリのインストール

Rubyで、XMLドキュメントの要素・属性値を取得・変更するときには、nokogiriライブラリを使います。
また、MIMEタイプを取得するためには、mime-typesライブラリを使います。
ということで、この2つのライブラリをインストールしておいてください。

必要なライブラリがインストールされているか確認。インストールされていない場合は、何も表示されません。

$ gem list | grep '\(nokogiri\)\|\(mime-types\)'

nokogiriライブラリをインストール。

$ sudo gem install nokogiri

mime-typesライブラリをインストール。

$ sudo gem install mime-types

必要なライブラリがインストールされているか確認。インストールされている場合は、次のように表示されます。

$ gem list | grep '\(nokogiri\)\|\(mime-types\)'
mime-types (3.1)
mime-types-data (3.2016.0521)
nokogiri (1.6.6.2)

svg-image-base64.rb

nokogiriとmime-typesライブラリを使えば、base64コードへの置換自動化が実現できます。

svg-image-base64.rb
require 'nokogiri'
require 'base64'
require 'mime/types'

def imgfile2base64(imgfile)
  Base64.strict_encode64(File.new(imgfile).read)
end

def image_mimetype(filename)
  MIME::Types.type_for(filename).each do |mime|
    return mime if mime.media_type == "image"
  end
  nil
end


docpath = ARGV[0]
if not File.exist?(docpath)
  STDERR.printf "Error: file %s is not found.", docpath
  exit 1
end
doc = Nokogiri::XML(File.open(docpath))
Dir.chdir(File.dirname(docpath)) do
  doc.css('image').each do |node|
    imgfile = node.attributes["href"].value
    if not File.exist?(imgfile)
      STDERR.printf "Warn: image file %s in %s is not exist.", imgfile, docpath
    else
      mimetype = image_mimetype(imgfile)
      if mimetype.nil?
        STDERR.printf "Warn: %s is not image file.", imgfile
      else
        node.attributes["href"].value = 'data:' << mimetype << ';base64,' << imgfile2base64(imgfile)
      end
    end
  end
end
print doc.to_s

svg-image-base64.rbの保存後、シェルなどから1番目の引数にSVGファイル(ここでは、a.svg)を指定し、次のように実行します。
SVGファイルのimage要素のxlink:href属性に記載されているファイル名をbase64コードに置換した結果が標準出力に出力されます。
ここではシェルのリダイレクト機能を使い、ab.svgファイルを作成しています。

ruby svg-image-base64.rb a.svg >ab.svg

実行後、ab.svgを直接開いてa.svgと同じ図が表示されること、HTMLファイルからimg要素を使って参照したときに正常に表示されることを確認してください。

まとめ

SVGファイルで、image要素を使って外部の画像ファイルを参照すると、問題が発生する場合があります。
その対策として、画像ファイルをbase64コードに変換したデータに置換します。こうした置換は、自動化できます。

14
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
14
Help us understand the problem. What is going on with this article?