プロジェクト

全般

プロフィール

20210214

実に半年ぶりぐらい。大体こんなことをやろうとしている

出発

Snipping ToolsはCtrl + C でキャプチャした画像をクリップボードに入れられる。
いちいちファイル保存を経由して、MarkdownEditorに貼るのは面倒なので
クリップボードに保管した画像をDATA URIスキームで貼り付けたいというひと手間省くためだけの願望を実現しようと色々調べてみた結果。

以下、記事のお品書き

  1. Webとクリップボード
  2. PowerShellとクリップボード
  3. .NETのImageのbase64エンコーディング
  4. PowerShellスクリプト

Webでのクリップボード

願望からやらなきゃいけないことは

  • Clipboardのデータを読む
  • 生データをbase64エンコードする

主に前者についてのお話。
今まで使っていたexecCommandはいつ削除されてもおかしくないらしい。なんということ。

という訳でクリップボードの正しい扱い方について情報収集していった。代替となりそうなW3C Clipboard APIだけどまだ草案段階でMDNのドキュメントも反映されきってないという素敵な状況。
まだ使えないなぁということで色々ぐぐってたら出てきたのがpaste.js: https://github.com/layerssss/paste.js 先人は偉大。
ソース眺めた感じ、Navigator.clipboardオブジェクトを基本的に使いつつ、Firefox対応の処理を入れている感じ。よさげ。pasteImageなるイベントも実装してくれてるし。

PowerShellとクリップボード

さて、paste.jsで実装しようかなと思ったんだけど、「クリップボードの画像をbase64エンコードする」ってブラウザでやるべきことか?とか疑問が沸いてきたため、例えばPowerShellとか使う方法を考えてみる。

  1. クリップボードから画像を読む
  2. 読んだ画像をbase64エンコードする
  3. base64文字列をクリップボードに入れる

とかそんな処理を書けばいいのかなと。ブラウザでやるとセキュリティとかメンドウクサイ。
とりあえず、使えそうなコマンドレットは以下二つ。

  • Get-Clipboard
  • Set-Clipboard

Set-Clipboardは今回見ない。

リファレンス( https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-clipboard?view=powershell-5.1 )によると-Formatオプションでテキストだの画像だの音声だのと指定できるらしい、が、PowerShell 7ではこのオプション削除されているらしいのは注意。Windows想定だからPowerShell 5.1で良いや。

コマンド結果:

PS $ Get-Clipboard -Format Image  |sv image
PS $ $image.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Bitmap                                   System.Drawing.Image

なんかBitmapクラスとかいうよくわからんのが出来たので調べよう。後参考( https://www.haruru29.net/blog/how-to-do-clipboard-operations-using-powershell/ )によるとリソースの開放とか気を付けたほうが良いみたい。まぁそうか。

.NETのImageのbase64エンコーディング

なんか以前、System.Text.Encodingクラスとかからテキストをバイト列にしたことはあったな。そう確かPowerShell 5.1 でUTF-8で保存すると、ついてしまうBOMを外すためにやった。今回は画像なのでちょっと違うなぁ。
結局のところバイトストリームを得られればいいんじゃないかな。
ImageクラスのSaveMemoryStream(Stream,ImageFormat)でMemoryStreamに保存、バイト列化してConvertクラスでbase64エンコーディングとかでいけそうかなぁ。

PowerShellスクリプト

というわけでできた。以下のように選べるようにしてみた。

  • 出力先:パイプライン・クリップボード
  • 出力形式:base64文字列・Markdownの画像・DataURIスキーム
function ClipImageToBase64() {
    Param(
        [string]
        [ValidateSet('base64', 'Markdown', 'dataURI')]
        $Format = 'base64',
        [switch]
        $Clip
    )   
    $image = Get-Clipboard -Format Image;
    $ms = New-Object -TypeName System.IO.MemoryStream;
    $image.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png);

    $encoded = [System.Convert]::ToBase64String($ms.GetBuffer());
    $image.Dispose();
    $ms.Dispose();

    switch ($format) {
        'base64' {
            $dataUri = $encoded;
        }
        'dataURI' {
            $dataUri = "data:image/png;base64," + $encoded;
        }
        'Markdown' {
            $dataUri = "![](data:image/png;base64," + $encoded + ")";
        }
    }

    if($Clip) {
        $dataUri | Set-Clipboard
    }
}

参考