Rubyの世界にはワクワクがいっぱい! あれ?
2008年8月20日 (水)なんか一月前は「Javaの世界には…」と言っていた気がしますが、今は目の前の Rubyに集中、集中! 今回は、Rubyの追加モジュールを使って Blog Editor の拡張コマンドを作ってみましょう。キラッ☆
その前に、「ScriptEngine プラグイン for ExtCommander」 についての大事なお知らせです。ExtCommander のフレームワークを便利に使ってもらえるように、いくつかのオブジェクトをあらかじめ登録してあります。
_owner |
Windowオブジェクト。これを親に指定してダイアログを開いてください。 |
_selectedstring |
エントリー編集画面で選択されている文字列。 |
_xmlstring |
エントリーデータのXML文字列。 |
Rubyの場合は $_owner などとして参照できます。ダイアログを開く際などに必要になりますので、覚えておいてください。_selectedstring を使うと、ウィキペディアリンク のような拡張コマンドが作成できます。_xmlstring はXMLですので、使用する場合はパーズする必要があります。ブラウザープレビュープラグイン はこのデータを使用しています。
さて、Ruby (や他のスクリプト言語)が人気がある理由の1つは『豊富な外部拡張モジュールを容易に追加できる』ということではないかと思っています。今回は、GEM で追加できる exifr と rmagick4j というモジュールを使ったサンプルをご紹介します。
exifr を使ったサンプル。 EXIF情報付きで写真をブログに貼り付けられます。 | |
rmagick4j を使ったサンプル。 ポラロイド写真風に画像を加工できます。 |
![]() |
では、サンプルスクリプトをご紹介します!
exifr と rmagick4j は、事前にGEMでインストールしておいてくださいね。詳細は略!
(gem install exifr とか gem install rmagick4j とかやるだけ)
次に、サンプルスクリプトです。2つあります。ちょぉっと長いですが、「隣のRuby-ist」氏の力作ですので、是非実行してみてください。
まず、デジカメの写真からEXIF情報を取得して、写真と一緒にブログに貼り付けるサンプルです。
exif_sample.rb
$KCODE = "UTF8"
require 'rubygems'
require 'exifr'
require 'date'
require 'java'
include_class('java.io.File') {|package,name| "J#{name}"}
include_class 'javax.swing.JDialog'
include_class 'javax.swing.JPanel'
include_class 'javax.swing.JLabel'
include_class 'javax.swing.JButton'
include_class 'javax.swing.JTextField'
include_class 'javax.swing.JFileChooser'
#
#=HTMLの取得. 引数 _image_path_, _width_, _height_ には画像へのファイルパス、表示したい幅と高さを指定します。
#
def get_html(image_path, width, height = "auto")
width = width + "px"
height = height + "px" unless height == "auto"
image_html = <<EOL
<a href="file:/#{image_path}">
<img src="file:/#{image_path}" alt="" style="width:#{width};height:#{height};" />
</a>
EOL
html = ""
begin
exif_info = EXIFR::JPEG.new(image_path)
rescue
html = "<div style=\"text-align:center;\">#{image_html}</div>"
return html
end
if exif_info.exif? then
date_time = DateTime.parse(exif_info.date_time_original.to_s)
html = <<EOL
<div style="margin:1em;text-align:center;">#{image_html}<br />
<table style="margin-left:auto;margin-right:auto;">
<caption>#{File.basename(image_path, ".*")}</caption>
<tbody>
<tr><th>カメラ</th><td>#{exif_info.make} #{exif_info.model}</td></tr>
<tr><th>撮影日時</th><td>#{date_time.strftime("%Y/%m/%d %H:%M")}</td></tr>
<tr><th>シャッター速度</th><td>#{exif_info.exposure_time.to_s}</td></tr>
<tr><th>露出</th><td>#{exif_info.f_number.to_f}</td></tr>
<tr><th>焦点距離(35mm換算)</th><td>#{exif_info.focal_length_in_35mm_film}mm</td></tr>
<tr><th>ISO</th><td>#{exif_info.iso_speed_ratings}</td></tr>
</tbody>
</table>
</div>
EOL
else
html = "<div style=\"text-align:center;\">#{image_html}</div>"
end
return html
end
#
#=JPEG画像ファイルフィルタ
#
class JpegFilter < javax.swing.filechooser.FileFilter
def accept(file)
return true if file.isDirectory
filename = file.getName
extname = File.extname(filename).downcase
if extname == ".jpg" || extname == ".jpeg"
return true
else
return false
end
end
def getDescription
return "JPEG images(*.jpg, *.jpeg)"
end
end
#
#=JPEG画像のExif情報を表示する
#
dialog = JDialog.new($_owner, "Exif Sample", true)
panel = JPanel.new(java.awt.FlowLayout.new)
button = JButton.new("Image Select")
width_label = JLabel.new("WIDTH")
width_field = JTextField.new("320", 4)
# ボタンクリック時の動作
button.addActionListener do |event|
chooser = JFileChooser.new
chooser.addChoosableFileFilter(JpegFilter.new)
chooser.setMultiSelectionEnabled(true)
chooser.setFileSelectionMode(JFileChooser::FILES_ONLY)
selected = chooser.showOpenDialog(dialog)
if selected == JFileChooser::APPROVE_OPTION then
files = chooser.getSelectedFiles
files.each do |file|
puts get_html(file.getAbsolutePath, width_field.getText)
end
dialog.dispose
end
end
# 画像表示幅の値補正
width_field.addFocusListener do |event|
event.source.setText(event.source.getText.to_i.to_s)
event.source.setText("320") unless event.source.getText.to_i > 0
end
panel.add(width_label)
panel.add(width_field)
panel.add(button)
dialog.add(panel)
dialog.setDefaultCloseOperation(JDialog::DISPOSE_ON_CLOSE)
dialog.setSize(320, 80)
dialog.setLocationRelativeTo(nil)
dialog.setVisible(true)
こんなダイアログが出たら、「Image Select」ボタンを押してデジカメ画像を選びます。

すると、次のような形式のHTMLを生成するので、それをエントリーに貼り付けることができます。
次に、ポラロイド風の画像を作成するサンプルです。
rmagick_sample.rb
$KCODE="UTF8"
require 'rubygems'
gem PLATFORM == 'java' ? 'rmagick4j' : 'rmagick'
require 'RMagick'
require 'tempfile'
require 'java'
include_class 'javax.swing.JDialog'
include_class 'javax.swing.JPanel'
include_class 'javax.swing.JLabel'
include_class 'javax.swing.JButton'
include_class 'javax.swing.JTextField'
include_class 'javax.swing.JFileChooser'
include_class 'javax.swing.JColorChooser'
include_class 'java.awt.dnd.DnDConstants'
include_class 'java.awt.datatransfer.DataFlavor'
include_class 'javax.swing.ImageIcon'
#
#=インスタント写真風画像作成
#
class ImagePoraroid
def initialize(filename = "")
set_image(filename) unless filename == ""
end
def set_image(filename)
@image = Magick::Image.read(filename).first
end
#
#インスタント写真風画像作成
#
def polaroid(width, title = "", font_size = 24, background_color = "#ffffff")
image = @image
# 画像リサイズ
image = image.resize_to_fit(width) unless width == image.columns
# 画像フォーマットをPNGに変換
image.format = "PNG" unless image.format == "PNG"
# ボーダー作成
image = self.border(image)
# テキスト作成
image = self.annotate(image, title, font_size) unless title == ""
# ドロップシャドウ作成
image = self.shadow(image, background_color)
# 回転
image.background_color = "red"
image.rotate!(-5) do
self.background_color = "red"
end
return image
end
#
#=インスタント写真風画像作成
#
def polaroid!(width, title = "", font_size = 24, background_color = "#ffffff")
@image = self.polaroid(width, title, font_size, background_color)
end
#
# ボーダー作成
#
def border(image)
border_width = 20
border_height = 60
background = Magick::Image.new(image.columns + border_width,
image.rows + border_height) do
self.background_color = "#fcfcfa"
end
border_x_offset = 10
border_y_offset = 10
image = background.composite(image,
border_x_offset,
border_y_offset,
Magick::OverCompositeOp)
return image
end
#
# テキスト作成
#
def annotate(image, str, font_size)
draw = Magick::Draw.new
margin_bottom = 10
draw.annotate(image, 0, 0, 0, margin_bottom, str) do
self.pointsize = font_size.to_i
self.gravity = Magick::SouthGravity
# self.font_family = "Arial"
self.fill = 'black'
end
return image
end
#
# ドロップシャドウ作成
#
def shadow(image, background_color = "#ffffff")
shadow_x_offset = 10
shadow_y_offset = 10
shadow_border_width = 5
shadow_border_height = 5
background_width = image.columns + shadow_x_offset + shadow_border_width
background_height = image.rows + shadow_x_offset + shadow_border_height
background = Magick::Image.new(background_width, background_height) do
self.background_color = background_color
end
shadow = Magick::Image.new(image.columns, image.rows) do
self.background_color = "gray75"
end
background = background.composite(shadow,
shadow_x_offset,
shadow_y_offset,
Magick::OverCompositeOp)
background = background.blur_image(0, 4)
image = background.composite(image,
0,
0,
Magick::OverCompositeOp)
return image
end
#
# 画像ファイル保存
#
def image_write(filename, image = @image)
image.write(filename)
end
end
#
#=ダイアログボックス作成
#
class RDialog
include java.awt.dnd.DropTargetListener
def initialize
@target_image_path = ""
@destination_image_path = ""
@dialog = JDialog.new($_owner, "Beyond ImageEasy", true)
@panel = JPanel.new(java.awt.FlowLayout.new)
@image_button = JButton.new("Image")
@width_label = JLabel.new("Image Width")
@width_field = JTextField.new("200", 3)
@font_size_label = JLabel.new("Font Size")
@font_size_field = JTextField.new("14", 2)
@caption_label = JLabel.new("Caption")
@caption_field = JTextField.new("", 20)
@color_label = JLabel.new("Background Color")
@color_field = JTextField.new("#ffffff", 7)
@preview_button = JButton.new("Preview")
@preview_button.setEnabled(false)
@ok_button = JButton.new("OK")
@ok_button.setEnabled(false)
@image_label = JLabel.new(ImageIcon.new)
@preview_panel = JPanel.new(java.awt.FlowLayout.new)
@preview_panel.setBackground(java.awt.Color::WHITE)
# 各ボタンクリック時の動作
@preview_button.addActionListener do |event|
preview(@target_image_path) unless @target_image_path == ""
end
@ok_button.addActionListener do |event|
ok_btn
end
@image_button.addActionListener do |event|
# ファイル選択
chooser = JFileChooser.new
chooser.addChoosableFileFilter(ImageFilter.new)
chooser.setFileSelectionMode(JFileChooser::FILES_ONLY)
selected = chooser.showOpenDialog(@dialog)
if selected == JFileChooser::APPROVE_OPTION then
file = chooser.getSelectedFile
@target_image_path = file.getAbsolutePath
preview(@target_image_path)
@preview_button.setEnabled(true) if @preview_button.isEnabled == false
end
end
# 画像幅の値補正
@width_field.addFocusListener do |event|
event.source.setText(event.source.getText.to_i.to_s)
event.source.setText("200") unless event.source.getText.to_i > 0
end
# フォントサイズの値補正
@font_size_field.addFocusListener do |event|
event.source.setText(event.source.getText.to_i.to_s)
event.source.setText("14") unless event.source.getText.to_i > 0
end
# 背景カラーの値補正
@color_field.addFocusListener do |event|
event.source.setText(event.source.getText.strip)
event.source.setText("#ffffff") unless event.source.getText =~ /^#[\da-f]{6}/
end
# ドラッグ&ドロップ処理登録
target = java.awt.dnd.DropTarget.new
target.addDropTargetListener(self)
@dialog.setDropTarget(target)
@panel.add(@width_label)
@panel.add(@width_field)
@panel.add(@font_size_label)
@panel.add(@font_size_field)
@panel.add(@caption_label)
@panel.add(@caption_field)
@panel.add(@color_label)
@panel.add(@color_field)
@panel.add(@image_button)
@panel.add(@preview_button)
@panel.add(@ok_button)
@preview_panel.add(@image_label)
@panel.add(@preview_panel)
@dialog.add(@panel)
@dialog.setDefaultCloseOperation(JDialog::DISPOSE_ON_CLOSE)
@dialog.setSize(500, 400)
@dialog.setLocationRelativeTo(nil)
@dialog.setVisible(true)
end
#
# 画像ファイルかどうか拡張子で判別
#
def image_file?(file_path)
extensions = [".png", ".jpg", ".jpeg", ".gif"]
extname = File.extname(file_path).downcase
if extensions.include?(extname) then
return true
else
return false
end
end
#
# プレビュー
#
def preview(file_path)
unless file_path == ""
image_width = @width_field.getText.to_i
caption = @caption_field.getText
font_size = @font_size_field.getText.to_i
color = @color_field.getText.strip
ip = ImagePoraroid.new(file_path)
# ポラロイド風画像処理
ip.polaroid!(image_width, caption, font_size, color)
# 画像ファイル保存
tmp_filename = "image" + Time.now.to_i.to_s + ".png"
@destination_image_path = File.join(Dir.tmpdir, tmp_filename)
ip.image_write(@destination_image_path)
# 画像表示
image_icon = ImageIcon.new(@destination_image_path)
@image_label.setIcon(image_icon)
@ok_button.setEnabled(true) if @ok_button.isEnabled == false
@preview_panel.setBackground(java.awt.Color.decode(color))
end
end
def ok_btn
# XHTML出力
print "<img src=\"file:/#{@destination_image_path}\" alt=\"\" />"
@dialog.dispose
end
def dragEnter(event)
transfer = event.getTransferable
if transfer.isDataFlavorSupported(DataFlavor::javaFileListFlavor) then
files = transfer.getTransferData(DataFlavor::javaFileListFlavor)
files.each do |file|
event.rejectDrag unless image_file?(file.getPath)
end
else
event.rejectDrag
end
end
def dragExit(event)
end
def dragOver(event)
end
def dropActionChanged(event)
end
def drop(event)
transfer = event.getTransferable
if transfer.isDataFlavorSupported(DataFlavor::javaFileListFlavor) then
event.acceptDrop(DnDConstants::ACTION_COPY_OR_MOVE)
files = transfer.getTransferData(DataFlavor::javaFileListFlavor)
# 複数のファイルがドラッグされていても最初のファイルのみ処理
@target_image_path = files.first.absolute_path if image_file?(files.first.path)
preview(@target_image_path)
@preview_button.setEnabled(true) if @preview_button.isEnabled == false
end
end
#
#=画像ファイルフィルタ
#
class ImageFilter < javax.swing.filechooser.FileFilter
def accept(file)
return true if file.isDirectory
filename = file.getName
extname = File.extname(filename).downcase
extensions = [".jpg", ".jpeg", ".png", ".gif"]
if extensions.include?(extname) then
return true
else
return false
end
end
def getDescription
return "images(*.jpg, *.jpeg, *.png, *.gif)"
end
end
end
RDialog.new
こんなダイアログが表示されたら、「Image」ボタンを押して画像ファイルを選択してください。「Caption」に文字列を入力して「Preview」ボタンを押せば文字列も書き加えられます。

「OK」ボタンを押すと次のような画像が作成されて、エントリーに貼り付けることができます。

これらのスクリプトは、コピー&ペーストしてScriptEngine プラグインのスクリプト領域に貼り付けて実行することもできるし、スクリプトをファイルに保存して「スクリプトからファイルを読み込み」メニューで読み込んで実行してもOKです。「貼り付け」ボタンを押すと実行した結果をエントリーに貼り付けることができます。
スクリプトなので、自分好みに改変することも簡単にできます。rmagick_sample.rb での画像回転角度を変えたりフォントを変えたり、自由にいじってみてください。
いやぁ、スクリプトって便利ですね。
しか~し!
拡張コマンドとして考えると、コマンドを実行するためにいちいちスクリプトを貼り付けるのは面倒ですよね。次回はもっと簡単に拡張コマンドとして使える方法をご紹介します。「メイクアップ:スクリプト編」です。


コメント