この記事は、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の標準添付ライブラリです。
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コードへの置換自動化が実現できます。
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コードに変換したデータに置換します。こうした置換は、自動化できます。