クリップボードの中身をHTMLエスケープする

公開:2013-03-09 07:46
更新:2017-11-15 21:58
カテゴリ:c++,アプリ・ライブラリ・言語,javascript

Syntax Highliterでソースコードを表示するときは、エスケープ処理してから張り付けなくてはいけない。そうしないと、たとえばC++のテンプレート定義とかはHTMLタグとして認識されてしまうからだ。今まではエディタの置換処理でエスケープしていたのだけれど、面倒くさいので、クリップボードにコピーしたものをエスケープするコードを書いてみた。

JavaScript(WSH)で行う方法

手っ取り早くJavaScriptで作れないかとググってみると「WSH よりクリップボード、次の手」というページに行き着いた。IE経由でクリップボードにアクセスする方法だ。これを使って作ったのが下のプログラムである。


<job id="EscapeClipboardText">
<script language="JScript">
var jp;
if (!jp) jp = {};
if (!jp.raindrop) jp.raindrop = {};
if (!jp.raindrop.frog) jp.raindrop.frog = {};
jp.raindrop.frog.clipboard || (function ()
    {
        // コマンドのID
        var OLECMDID_COPY = 12;
        var OLECMDID_PASTE = 13;
        var OLECMDID_SELECTALL = 17;

        // IE の初期化
        var _internetExplorer = new ActiveXObject ('InternetExplorer.Application');
        _internetExplorer.navigate ("about:blank");
        while (_internetExplorer.Busy)
            WScript.Sleep (10);

        // textarea 要素を作成する
        var _textarea = _internetExplorer.document.createElement ("textarea");
        _internetExplorer.document.body.appendChild (_textarea);
        _textarea.focus ();

        jp.raindrop.frog.clipboard = {
            // クリップボードに文字列をコピー
            setText: function (text)
            {
                _textarea.innerText = text;
                _internetExplorer.execWB (OLECMDID_SELECTALL, 0);
                _internetExplorer.execWB (OLECMDID_COPY, 0);
            },

            // クリップボードより文字列を取得
            getText: function ()
            {
                _textarea.innerText = "";
                _internetExplorer.execWB (OLECMDID_PASTE, 0);
                return _textarea.innerText;
            },

            // IE を解放
            release: function ()
            {
                _internetExplorer.Quit ();
            }
        };
    }());

var data = jp.raindrop.frog.clipboard.getText ();
data = data.replace(/\&/ig,'&');
data = data.replace(/\>/ig,'>');
data = data.replace(/\</ig,'<');
data = data.replace(/\"/ig,'"');
jp.raindrop.frog.clipboard.setText (data);

</script>
</job>

しかし私の環境で実行すると下記ウィンドウが表示されてしまう。しかしエスケープはきちんとされていた。

ブラウザがIE10のせいかもしれないね。。さてどうしたものか。。

C++で作り直す

考え直すとそもそもクリップボードAPIを直接使うほうが簡単じゃないか。C++でつくればすぐできるなということで作ったのが下記コードである。文字列置換部分はS34サイトの「’置換’はどうやればいいのですか?」を 参考にした。


//
// escapeclipboard.cpp : クリップボードの中身をエスケープする
//

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <WindowsX.h>
#include <string>

std::wstring replace(const std::wstring& source,const std::wstring& pattern,const std::wstring& placement){
std::wstring result(source);
  for ( std::wstring::size_type pos = 0 ;
        std::wstring::npos != (pos = result.find(pattern,pos));
        pos += placement.size() )
    result.replace(pos, pattern.size(), placement);
  return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
  ::OpenClipboard(NULL);
  HANDLE text = GetClipboardData(CF_UNICODETEXT);
  if(text)
  {
      std::wstring clip_text((wchar_t*)::GlobalLock(text));
      GlobalUnlock(text);
      std::wstring replaced_data;

      replaced_data = replace(clip_text,L"&",L"&");
      replaced_data = replace(replaced_data,L"<",L"<");
      replaced_data = replace(replaced_data,L">",L">");
      replaced_data = replace(replaced_data,L"\"",L""");
      replaced_data = replace(replaced_data,L"'",L"'");

      HGLOBAL replaced_data_handle = ::GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,sizeof(wchar_t) * (replaced_data.length() + 1));
      wchar_t* dest = (wchar_t*)GlobalLock(replaced_data_handle);
      ZeroMemory(dest,sizeof(wchar_t) * (replaced_data.length() + 1));
      std::copy(replaced_data.begin(),replaced_data.end(),dest);
      GlobalUnlock(replaced_data_handle);
      EmptyClipboard();
      SetClipboardData(CF_UNICODETEXT,replaced_data_handle);
      CloseClipboard();
  }
    return 0;
}

こっちのほうが簡単にできたな。。