クリップボードから画像の貼り付け

(87d) 更新

公開メモ

pukiwiki の編集画面に Ctrl+v で画像を貼り付ける

「クリップボードの画像ファイルをアップロードする」
http://qiita.com/volpe28v@github/items/dc100c75c2371bb82a3b

という記事を参考にしました。

結果として、

  1. クリップボード内の画像データをページに添付して、
  2. 添付ファイルへのリンクを張る、

という一連の動作を自動化できました。

これまでであれば、

  1. クリップボード内の画像データをファイルとしてPCに保存する
  2. 保存したファイルをページに添付する
  3. 添付したファイルへのリンクをソースに埋め込む
  4. PC上に残った保存したファイルを消す

という作業が必要だったのと比べると作業効率が大きく向上します。

スクリーンキャプチャーを多数貼り込むようなページを書くのにとても重宝しそうです。

制限

今のところ Chrome でしか動作確認が取れていません。

Firefox などは最新の Clipboard API に対応していないそうです。

使い方

ソースコード編集画面は見た目上、何も変りません。

  編集画面.png

クリップボードに画像がある状態で、右クリックから「貼り付け」を選択するか、 Ctrl+v を押すと、ファイル名を聞くダイアログが表示されます。 ダイアログで Cancel を押せば貼り付けはされません。

  ダイアログ.png

ダイアログに入力した名前で画像がアップロードされ、 アップロードされた画像へのリンクが貼り付けた位置に挿入されます。

  リンク.png

画像として表示するには、拡張子を .png としておく必要があるようです。

処理の基本的な流れ

  1. ソース編集用の textarea の paste イベントを拾う
  2. clipboardData.items から画像データを探す
  3. getAsFile() でデータを取り出す
  4. window.prompt でファイル名の入力を促す
  5. new FormData() で attach プラグインに渡すフォームデータを作成する
  6. jQuery.ajax でフォームデータを post する

ソースファイル

lib/html.php の edit_form 関数の中で $body = <<<EOD を探し、 対応する EOD の直前に以下のコードを貼り付けます。

LANGUAGE: html
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.js"></script>
<script>

// from http://qiita.com/volpe28v@github/items/dc100c75c2371bb82a3b
$("textarea[name='msg']").on('paste', function(event){
  // event からクリップボードのアイテムを取り出す
  var items = (event.clipboardData || event.originalEvent.clipboardData).items; // ここがミソ
  for (var i = 0 ; i < items.length ; i++) {
    var item = items[i];
    if (item.type.indexOf("image") != -1) {
      // 画像データがあればページに添付する
      var image = item.getAsFile();
      upload_file_with_ajax(image);
    }
  }
});

// ファイルアップロード
function upload_file_with_ajax(file){
    var img_name = window.prompt("Image file name", "noname.png");
    if(img_name==null) return;

    var formData = new FormData();

    // from http://hakuhin.jp/js/form_data.html#FORM_DATA_02
    formData.append("attach_file", file, img_name);
    formData.append("encode_hint", "ぷ");
    formData.append("plugin", "attach");
    formData.append("pcmd", "post");
    formData.append("refer", "$s_page");

    $.ajax('$script' , {
      type: 'POST',
      contentType: false,
      processData: false,
      data: formData,
      error: function() {
        // アップロードエラー処理
        beep();
      },
      success: function(res) {
        insertAtCaret($("textarea[name='msg']"),
          "&ref("+img_name+");");
      }
    });
}

// textarea の選択位置に文字列を挿入する
// from http://d.hatena.ne.jp/spitfire_tree/20131209/1386575758
function insertAtCaret(target, str) {
  var obj = $(target);
  obj.focus();
  if(navigator.userAgent.match(/MSIE/)) {
    var r = document.selection.createRange();
    r.text = str;
    r.select();
  } else {
    var s = obj.val();
    var p = obj.get(0).selectionStart;
    var np = p + str.length;
    obj.val(s.substr(0, p) + str + s.substr(p));
    obj.get(0).setSelectionRange(np, np);
  }
}

// ビープ音を鳴らす
// from http://qiita.com/isseium/items/12b215b6eab26acd2afe
function beep()
{
  new Audio(
    "data:audio/wav;base64,"+
    "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"+
    "U291bmQgRm9yZ2UgNC41AA==").play();
}

</script>

Javascript のデバッグ

うまく動かない場合があるとの書き込みをいただきましたので、 javascript に問題がある場合にどのように確認すれば良いかを 書いておこうと思います。

ここでは Chrome を使います。

まず、Ctrl+Shift+I を押すと、画面下部に開発者ツールが開きます。

[Source] タブを開いて、左側で "?plugin=edit&..." を選ぶと、 編集画面のソースコードが表示されます。

上記コードに対応する場所を探して、on('paste', function(event){ の後の var items = の行の行番号部分をクリックすると青くなって、 ブレークポイントが設定されます。(下図)

setting-break-point.png

こうしておいてから、適当な画像をクリップボードに入れて Ctrl+V を押すと、

paused-at-break-point.png

のように指定箇所でブレークしますので、あとは右側の debug-tools.png あたりのコントロールや Watch, Call Stack などを使ってデバッグできます。

質問・コメント




pukiwiki の編集画面に Ctrl+v で画像を貼り付ける機能について

カーティン? ()

管理人様

はじめまして。カーティンと申します。

大変便利そうな機能でしたので、導入させて頂こうと思い
インストール手順に従い、lib/html.phpに下記のように追記しました。


$body = <<<EOD
<div class="edit_form">

(中略)

</div>


-----(追加分ここから)--------
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.js"></script>
<script>

(中略)

"U291bmQgRm9yZ2UgNC41AA==").play();
}
</script>


-----(追加分ここまで)--------

EOD;


上記を行った後、クリップボード上にpngデータがある状態で貼り付け・Ctrl+Vのどちらも試みましたが
ダイアログの表示が出てこず、機能が動作しませんでした。

もし、原因となりそうなことがありましたら、ご教示頂けると幸いです。

■検証環境
ブラウザ:Google Chrome
サーバー:pukiwiki 1.5.1 PHP7.0.9(5.3.3でも確認)

  • クリップボードに bmp がある状態でもダメでしょうか? Windows では png を直接クリップボードにいれてもうまく認識されない気がします。また、Chrome では Ctrl+Shift+I で javascript のデバッガが開きます。そちらではどこまで処理が進んでいるでしょうか?Ctrl+V からダイアログが出るところまでは javascript 上でしか処理は走っていませんので、pukiwiki や php 側の問題では無いはずです。現在のバージョンでは Firefox でも同じコードで動作しています。 -- 武内(管理人)?
  • お世話になります。クリップボードに bmpがあることは確認しました。その状態で、Ctrl+Vまたは貼り付けをしても反映されません。javascriptのデバッグに関しては知識がないので、どこまでコードが走っているか確認は出来ておりません。Firefoxでも確認致しましたがダイアログが表示されず動作しておりません。コードを挿入する場所がおかしいのでしょうか? -- カーティン?
  • javascript のデバッグ方法について記事中に追記しました。参考になればうれしいです。 -- 武内(管理人)?
  • ご丁寧にありがとうございます。 ご提供頂きました、デバッグ方法にてデバッグを行ったところ、https://gyazo.com/540c9c9a5a26ee1295082cc8643cfbed ページにアクセスした際に、上記の画像 $("textarea[name='msg']").on('paste', function(event){ の処理で外に抜けてしまうだけで クリップボードを空の状態→クリップボードに画像を保存しても、var items = (event.clipboardData || event.originalEvent.clipboardData).items; のブレークポイントにひっかかることがありませんでした。 色々と見てみたのですが、原因が分からないためお力添え頂ければ幸いです。 -- カーティン?
  • お使いの pukiwiki の編集画面のエディタは pukiwiki オリジナルのものでしょうか?何か特殊なエディタを入れているのだとすると、そちらの javascript コードとバッティングしている可能性があります。 -- 武内(管理人)?

Counter: 1197 (from 2010/06/03), today: 2, yesterday: 0