Category Archives: 未分類

2020-12-08

Emacs Lispでポップアップメニューを出す方法(x-popup-menuの使用例)

EmacsでGUIプログラミングをしていると何かをクリックしたときにポップアップメニューを出したくなることがあります。メニューボタンを押したとき、対象を右クリックしてコンテキストメニューを出したいとき、といった具合です。

そんなときに使えるのが x-popup-menu という関数です。

x-popup-menu関数の概要

29.17 Pop-Up Menus - Emacs Lisp Manual

Function: (x-popup-menu position menu )

position にはメニューの表示位置を指定します。具体的な位置を指定するリスト ((XOFFSET YOFFSET) WINDOW-OR-FRAME) の他、マウスイベントやtを指定出来ます。tの場合現在のマウス位置に表示します。nilの時は実際には表示せず何かを事前計算するらしいのですがよく分かりません(やってみると単にnilが返ってきます)。クリックに応じてポップメニューを出したい場合は、last-input-event(interactive "e")で取得したイベント、または t を指定すると良さそうです。プルダウンメニューのように位置が決まっているものは具体的な位置を計算した上指定する必要があるのでしょう。

menu にはキーマップ(またはキーマップのリスト)か、独自の形式のリスト(ペインのリスト)を指定できます。

x-popup-menuにペインのリストを指定する

menu にペインのリストを指定するのが一番簡単ですが出来ることは限られます。

例えば次のコードを評価すると……

(x-popup-menu t '("Menu Title"
                  ("Pane Title1"
                   ("Item1-1" . 11)
                   ("Item1-2" . 12))
                  ("Pane Title2"
                   ("Item2-1" . 21)
                   ("Item2-2" . 22))))

次のようなポップアップメニューが表示されます。

x-popup-menuに複数のペインを含むリストを指定した場合の表示
図1: x-popup-menuに複数のペインを含むリストを指定した場合の表示

項目を選択すると項目の値がそのまま返ってきます。上の例では「Item1-1」を選択すると11が返ってきます。選択しなかった場合はC-gを押したときのようにQuitシグナルが発生します。

複数のペインを持ちたくない場合はペインを一つだけ指定することになりますが、その場合ペインのタイトルは表示されません。次の例では「Pane Title1」は表示されません。

(x-popup-menu t '("Menu Title"
                  ("Pane Title1"
                   ("Item1-1" . 11)
                   ("Item1-2" . 12))))
x-popup-menuに一つのペインを含むリストを指定した場合の表示
図2: x-popup-menuに一つのペインを含むリストを指定した場合の表示

x-popup-menuにキーマップを指定する

x-popup-menuの機能を完全に利用するにはキーマップを指定する必要があります。

例えば次のコードを評価すると……

(defun test-item-1 () (interactive) (message "Menu Item 1"))
(defun test-item-2 () (interactive) (message "Menu Item 2"))
(setq test-item-toggle-selected t)
(defun test-item-toggle () (interactive) (setq test-item-toggle-selected (not test-item-toggle-selected)))
(setq test-radio-choice 1)
(defun test-item-radio-1 () (interactive) (setq test-radio-choice 1))
(defun test-item-radio-2 () (interactive) (setq test-radio-choice 2))
(defun test-submenu-1-2 () (interactive) (message "hello"))

(let* ((menu '(keymap "Menu Title"
                      ;;イベント型 拡張メニュー 項目名 コマンド プロパティ...
                      (test-item-1 menu-item "Item1" test-item-1)
                      (test-item-2 menu-item "Item2" test-item-2 :keys "HogeHogeKey")
                      (3 menu-item "Item3" test-item-3 :enable nil)
                      (4 menu-item "Item4" test-item-4 :visible nil)
                      (5 menu-item (concat "Item" "5") test-item-3 :enable nil)
                      (test-item-toggle menu-item "ItemToggle" test-item-5 :button (:toggle . test-item-toggle-selected))
                      (separator-1 menu-item "--")
                      (test-item-radio-1 menu-item "ItemRadio1" test-item-radio-1 :button (:radio . (= test-radio-choice 1)))
                      (test-item-radio-2 menu-item "ItemRadio2" test-item-radio-2 :button (:radio . (= test-radio-choice 2)))
                      (separator-2 menu-item "--")
                      (text-1 menu-item "選択できない項目")
                      (separator-3 menu-item "--")
                      (10 menu-item "About Emacs" about-emacs) ;; :keysを書かなくてもglobal-mapから割り出してくれる
                      (submenu-1 menu-item "Submenu" (keymap "Submenu Title"
                                                             (submenu-1-1 menu-item "SubItem1" test-subitem-1)
                                                             (test-submenu-1-2 menu-item "SubItem2" test-subitem-2)))))
       (result (x-popup-menu t menu)))
  ;; 結果の最後の要素が関数なら呼び出す
  (if (and (symbolp (car (last result))) (fboundp (car (last result))))
      (funcall (car (last result)))
    ;; 関数でなければそのまま表示してみる
    (message "result=%s" result)))

次のようなポップアップメニューが表示されます。

x-popup-menuにkeymapを指定した場合の表示
図3: x-popup-menuにkeymapを指定した場合の表示

このように様々な事が出来るようになっています。

詳しくはマニュアルを参照のこと。

少しだけ補足。

:keys は通常書く必要はありません。上の例では HogeHogeKey なんて存在しないキーを表示させてましたが、あくまで何でも書けるか試しただけです。特筆すべきは About Emacs のところ。何も指定していなくても C-h C-a と表示されています。コマンド名からキーシーケンスを自動的に割り出して表示してくれます。グローバルマップやローカルマップだけでなく、(ポイントがそこにあれば)テキストプロパティやオーバーレイのkeymapプロパティまで考慮してくれます。

x-popup-menuは選択結果を返すだけでコマンドの呼び出し等は行いません。

easymenu.elというのが標準で入っていてもう少し簡単にメニュー用のキーマップが定義できるのですが、easy-menu-defineでキーマップを作ると項目の一番最初の要素は項目名の文字列をシンボルにしたものになるのでx-popup-menuで選択するとその人間向けな感じのシンボルが返ってきてしまいます。そこから実際にコマンドを呼び出す方法が今ひとつ分かりません。おそらく返ってきたシンボルを使ってキーマップから関数を割り出して呼び出せということなんだと思いますが、それなら最初から関数シンボルが返ってきた方が楽そうですよね……。そうでもないのかな? キーマップの意味論的に最初の要素はキー、というかイベント(シンボルはその名前のファンクションキーを押したというイベントという意味)なのだから、ルックアップを挟むべきってことなのでしょうか。それならば、こんな感じ?

(easy-menu-define test-menu nil
  "This is a Test Menu."
  '("Test Menu Title"
     ["Forward word desu!" forward-word] ;;x-popup-menuの結果は '(Forward\ word\ desu!)
     ["Backward word desu!" backward-word] ;;結果は '(Backward\ word\ desu!)
     ["Fun" (lambda () (interactive) (message-box "Fun!"))] ;;結果は '(Fun)
     ("Sub"
      ["FunFun" (lambda () (interactive) (message-box "FunFun!"))] ;;結果は'(Sub FunFun)
     )))

(let* ((events (x-popup-menu t test-menu))
       (cmd (lookup-key test-menu (apply 'vector events))))
  (call-interactively cmd))

例えば Sub → FunFun と選ぶとx-popup-menuは (Sub FunFun) というリストを返してきます。これはSubキーを押してからFunFunキーを押すという架空のキーシーケンスを意味します。そのキーシーケンスを元にlookup-keyでキーマップtest-menuからコマンドを割り出しています。

lookup-keyはリストを受け付けてくれなかったのでvectorに変換してから渡しています。これで良いのかな……。

他にも探したら popup-menu なんていう関数がmenu-bar.elにありました。内部でx-popup-menuを使っているようですがちゃんとコマンド呼び出しまでやってくれます。Emacs Lispのマニュアルに書いてない関数だけどこれでいいっぽい?

(popup-menu test-menu)
2020-11-28

Emacs Lispで画像を表示する方法(Emacs 27.1で確認)

参考

画像の表示方法については、Emacs LispマニュアルのImagesの所で説明されている。

手順

Emacs Lispで画像を表示するには次の二つのステップを踏む。

  1. Image Descriptorを作成する
  2. Image Descriptorをオーバーレイまたはテキストプロパティに設定する

まず第一にImage Descriptorを作成する。Image Descriptorとは画像をどのように表示するかを指定するリストで41.17.2 Image Descriptorsに書かれている形式で記述する。自分で組み立てても良いが41.17.8 Defining Imagesに書かれている create-image 等の関数を使うこともできる。SVG画像を作る場合は41.17.6 SVG Imagesに書かれている関数を使うこともできる。

次に作成したImage Descriptorをオーバーレイ(41.9 Overlays)またはテキストプロパティ(33.19 Text Properties)の、displayプロパティ(41.16 The display Property)に設定する。それによってはじめて実際に画像が表示される。自分でオーバーレイやテキストプロパティを設定しても良いが41.17.9 Showing Imagesに書かれている insert-image 等の関数を使うこともできる。この関数はバッファ内に適当な(" "等の)文字列を挿入してそのテキストプロパティのdisplayプロパティにImage Descriptorを設定する。

実例

それではやってみよう。

まず適当な画像ファイルを用意して、それを読み込むようなImage Descriptorを作成する。次の式を評価するとImage Descriptorが返ってくる。

(create-image (expand-file-name "~/tmp/karasawa.jpg"))

結果は次のようなリストになる。 :scale 1 の部分は高DPI環境では異なるかもしれない(高DPI環境でscaleを大きくする機能がcreate-imageに備わっている)。

(image :type jpeg :file "/home/hoge/tmp/karasawa.jpg" :scale 1)

この時点ではまだ画像は読み込まれない。

次のようにすると実際に画像が表示される。

(insert-image (create-image (expand-file-name "~/tmp/karasawa.jpg")))

(2021-10-11追記: create-imageに引き渡すファイル名は絶対パスに変換する必要がある。相対パスを指定するとdata-directoryやx-bitmap-file-pathの中から画像を探すことになり、設定によっては画像が読み込まれない)

画像ファイルを表示
図1: 画像ファイルを表示

関数insert-imageは適当な文字列(" ")をバッファに挿入し、その文字列がある範囲のdisplayテキストプロパティにImage Descriptorを設定する。

なので、次のようにしても同じ結果が得られる。

(progn
  (insert " ")
  (put-text-property (1- (point)) (point) 'display (create-image (expand-file-name "~/tmp/karasawa.jpg"))))

適当な画像を用意するのが面倒ならばSVG画像を作る方法もある。次のコードを評価するとSVGデータを含んだImage Descriptorが返ってくる。

(require 'svg)
(let ((svg (svg-create 400 300)))
  (svg-rectangle svg 0 0 400 300 :fill "#69f")
  (svg-circle svg 200 150 100 :fill "#eee")
  (svg-image svg))
(image :type svg :data "<svg width=\"400\" height=\"300\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"> <rect width=\"400\" height=\"300\" x=\"0\" y=\"0\" fill=\"#69f\"></rect> <circle cx=\"200\" cy=\"150\" r=\"100\" fill=\"#eee\"></circle></svg>" :scale 1)

実際に表示させてみる。

(require 'svg)
(insert-image
 (let ((svg (svg-create 400 300)))
   (svg-rectangle svg 0 0 400 300 :fill "#69f")
   (svg-circle svg 200 150 100 :fill "#eee")
   (svg-image svg)))
SVG画像を表示した結果
図2: SVG画像を表示した結果

画像の変換

表示する際に簡単な画像処理も可能。

(insert-image (create-image (expand-file-name "~/tmp/karasawa.jpg") 'jpeg nil :width 300 :rotation 90 :relief -10))
縮小、回転、レリーフ
図3: 縮小、回転、レリーフ
(insert-image (create-image (expand-file-name "~/tmp/karasawa.jpg") 'jpeg nil :width 400 :conversion 'disabled))
disabled効果
図4: disabled効果

これらの変形・効果はSVG画像にも適用できる。おそらくSVGをレンダリングした後のビットマップに適用している。

この他にも41.17.2 Image Descriptorsにはいくつかのプロパティが説明されている。また、最終的な表示のされ方はsliceなど他のdisplayプロパティの影響も受ける。

GIFアニメーションの表示も可能。(see:41.17.10 Multi-Frame Images)

画像のクリック

画像がクリックされたときに何か処理を行うこともできる。

(require 'svg)

(let (;;適当な文字列("A")を追加しその範囲をstart,endとする。
      (start (point))
      (end (progn (insert "A") (point))))

  ;; displayプロパティにSVG画像を設定する。
  (put-text-property
   start end 'display
   (let ((svg (svg-create 200 40)))
     (svg-rectangle svg 0 0 200 40 :rx 10 :ry 10 :stroke "#888" :fill "#fff")
     (svg-text svg "Button" :x 100 :y 30 :font-size 35 :text-anchor "middle")
     (svg-image svg)))

  ;; keymapプロパティにマウスクリックの際に呼ばれる関数を設定する。
  (put-text-property
   start end 'keymap
   (let ((km (make-sparse-keymap)))
     (define-key km [mouse-1]
       (lambda (event)
         (interactive "e")
         ;;(message "(x . y)=%s" (posn-object-x-y (event-start event)))
         (message "clicked event=%s" event)))
     km))

  ;; pointerプロパティをhandにすることでマウスカーソルの形を変える。
  (put-text-property
   start end 'pointer 'hand))
画像を表示してクリックした結果
図5: 画像を表示してクリックした結果

マウスの左クリックは[mouse-1]をキーマップに設定することで検出できる。テキストプロパティのkeymapプロパティを設定することで、画像がある場所にだけ効果があるキーマップを設定できる。

クリックした位置などイベントの詳細情報はInteractive Codeの"e"か、 last-input-event で受け取れる。

イベントの詳細情報はリストの形で記録されている。(22.7.4 Click Events)

そこから画像内でのクリックされた位置を取り出すには次のようにする。(22.7.15 Accessing Mouse Events)

(let ((xy (posn-object-x-y (event-start event))))
  (format "x=%s y=%s" (car xy) (cdr xy)))

画像の一部をクリック

画像内の一部がクリックされたことを判定したい場合イベントの座標から割り出しても良いがImage Descriptor:mapプロパティを使うこともできる。

(require 'svg)

(let (;;適当な文字列("A")を追加しその範囲をstart,endとする。
      (start (point))
      (end (progn (insert "A") (point))))

  ;; displayプロパティにSVG画像を設定する。
  (put-text-property
   start end 'display
   (let ((svg (svg-create 400 300)))
     (svg-circle svg 100 150 30 :fill "red")
     (svg-circle svg 200 150 30 :fill "blue")
     (svg-circle svg 300 150 30 :fill "green")
     ;; SVG画像を作る。同時にImage Descriptorに:mapプロパティを付加する。
     (svg-image svg :map '(((circle . ((100 . 150) . 30)) red-area (pointer hand help-echo "red"))
                           ((circle . ((200 . 150) . 30)) blue-area (pointer hand help-echo "blue"))
                           ((circle . ((300 . 150) . 30)) green-area (pointer hand help-echo "green"))))))

  ;; keymapプロパティにマウスクリックの際に呼ばれる関数を設定する。
  (put-text-property
   start end 'keymap
   (let ((km (make-sparse-keymap)))
     (define-key km [red-area mouse-1] (lambda () (interactive) (message "red!")))
     (define-key km [blue-area mouse-1] (lambda () (interactive) (message "blue!")))
     (define-key km [green-area mouse-1] (lambda () (interactive) (message "green!")))
     km)))
mapプロパティを伴う画像をクリックした結果
図6: mapプロパティを伴う画像をクリックした結果

矩形、円、任意の多角形で当たり判定の領域を設定できる。

画像の自動スケーリング

関数 create-image には高解像度モニターに備えて画像を自動的に拡大する機能がある。

拡大率は変数 image-scaling-factor で設定できる。デフォルトは 'auto になっており、ウィンドウサイズとフォントサイズの比率によって計算する。

具体的な計算は関数 image-compute-scaling-factor で行われている。まず、ウィンドウのピクセル幅を横方向の文字数(桁数)で割った値を計算する(つまり、平均文字幅ピクセル数)。その値が10以上の時、10からどのくらい大きくなったかの比率が画像の拡大率となる。そうして計算された値がcreate-imageが返すImage Descriptorの:scaleプロパティに付加される。

create-imageのPROPS引数に :scale プロパティが指定されている場合はこの処理は行われない。

注意すべき点は、この拡大率はマウスイベントの座標には一切影響を及ぼさないということ。イベントから得られる座標はこの拡大率の影響を受けないフレーム上の純粋な座標となる。また、:mapプロパティもこの拡大率の影響を受けて調整されることは ない 。従ってこの自動スケーリングが機能してしまうと、座標や領域の位置が画像と食い違ってしまう。これが問題になる場合、 (image-compute-scaling-factor image-scaling-factor) の計算値を使ってマウス座標や:mapプロパティを変換する必要がある。

2020-10-27

2020年秋の新番組

ウィルス騒ぎの影響から少し戻ってきた感じがありますね。

09/25(金) 17:20~ NHK Eテレ かいじゅうステップ ワンダバダ 第2シリーズ
09/25(金) 19:00~ TOKYO MX 魔神英雄伝ワタル 七魂の龍神丸 1~4話イッキ見SP
10/–(-) ~ YouTube おねがいっパトロンさま!
10/01(木) 23:30~ TOKYO MX ひぐらしのなく頃に 新アニメ
10/01(木) 25:28~ TBS アサルトリリィ Bouquet
10/02(金) 21:54~ TOKYO MX 兄に付ける薬はない! 第4期
10/02(金) 22:00~ TOKYO MX 100万の命の上に俺は立っている
10/02(金) 22:30~ TOKYO MX 魔女の旅々
× 10/02(金) 24:00~ TOKYO MX ヒプノシスマイク -Division Rap Battle- Rhyme Anima
10/02(金) 24:30~ TOKYO MX ダンジョンに出会いを求めるのは間違っているだろうかIII
10/02(金) 25:00~ TOKYO MX レヱル・ロマネスク
10/02(金) 25:05~ TOKYO MX トニカクカワイイ
10/02(金) 25:23~ テレビ東京 キングスレイド-意志を継ぐものたち-
10/02(金) 25:25~ TBS系 呪術廻戦
10/02(金) 26:25~ TBS ハイキュー!! TO THE TOP(第4期)
10/02(金) ~ Amazon PV BURN THE WITCH
× 10/03(土) 05:30~ TBS 犬と猫どっちも飼ってると毎日たのしい
10/03(土) 09:30~ テレビ東京系 ドラゴンクエスト ダイの大冒険
10/03(土) 17:30~ 日本テレビ系 半妖の夜叉姫
10/03(土) 22:30~ TOKYO MX ラブライブ!虹ヶ咲学園スクールアイドル同好会
× 10/03(土) 23:30~ TOKYO MX 戦翼のシグルドリーヴァ
10/03(土) 24:30~ TOKYO MX 魔法科高校の劣等生 -来訪者編-(第2期)
10/03(土) 25:30~ TOKYO MX ギャルと恐竜
10/03(土) 26:00~ テレビ朝日系 いわかける!- Sport Climbing Girls -
10/04(日) 21:55~ BS12 かえるのピクルス
10/04(日) 22:00~ TOKYO MX 無能なナナ
10/04(日) 23:00~ TOKYO MX アイドリッシュセブン Second BEAT!
10/04(日) 23:30~ TOKYO MX 神達に拾われた男
10/04(日) 24:30~ TOKYO MX まえせつ!
× 10/04(日) 25:00~ TOKYO MX 大人にゃ恋の仕方がわからねぇ!
× 10/04(日) 25:05~ TOKYO MX エタニティ ~深夜の濡恋ちゃんねる~
× 10/04(日) 25:20~ TOKYO MX 秘密結社 鷹の爪 ~ゴールデン・スペル~
10/05(月) 23:00~ TOKYO MX ゴールデンカムイ 第三期
10/05(月) 25:05~ TOKYO MX One Room サードシーズン
10/05(月) 26:00~ テレビ東京 魔王城でおやすみ
10/06(火) 24:30~ TOKYO MX 池袋ウエストゲートパーク
10/07(水) 22:30~ TOKYO MX ツキウタ。 THE ANIMATION2
10/07(水) 23:30~ TOKYO MX くま クマ 熊 ベアー
10/07(水) 23:54~ BS11 せいぜいがんばれ!魔法少女くるみ 第3期
10/07(水) 24:00~ TOKYO MX NOBLESSE -ノブレス-
10/07(水) 25:05~ TOKYO MX 第501統合戦闘航空団 ストライクウィッチーズ ROAD to BERLIN
× 10/07(水) 25:35~ TOKYO MX キミと僕の最後の戦場、あるいは世界が始まる聖戦
10/08(木) 21:54~ TOKYO MX ぐらぶるっ!
10/08(木) 24:30~ TOKYO MX アクダマドライブ
10/08(木) 25:58~ TBS 安達としまむら
10/09(金) 07:30~ テレビ東京系 カピバラさん
× 10/09(金) 19:30~ TOKYO MX 最響カミズモード!
10/10(土) 22:00~ TOKYO MX ご注文はうさぎですか? BLOOM (第3期)
10/10(土) 24:00~ TOKYO MX 神様になった日
10/10(土) 25:30~ テレビ朝日系 体操ザムライ
10/11(日) 22:30~ TOKYO MX 憂国のモリアーティ
10/11(日) 24:30~ TOKYO MX まえせつ!
× 10/12(月) 22:30~ TOKYO MX おちこぼれフルーツタルト
10/12(月) 24:00~ TOKYO MX A3!(エースリー) -AUTUMN & WINTER-
10/12(月) 25:30~ テレビ東京 おそ松さん 第3期
10/12(月) 26:30~ テレビ東京 それだけがネック
× 10/13(火) 23:00~ TOKYO MX 禍つヴァールハイト
10/25(日) 20:30~ AT-X 刀使ノ巫女 刻みし一閃の燈火(前後編)
2020-09-06

2020年夏の新番組

もう9月になってしまったけれど。

07/01(月) 17:59~ TOKYO MX 耐え子の日常 新シリーズ
07/02(木) 23:30~ TOKYO MX HERO MASK
07/03(金) 22:00~ TOKYO MX 天晴爛漫
07/03(金) 24:00~ TOKYO MX ド級編隊エグゼロス
07/03(金) 24:30~ TOKYO MX 食戟のソーマ 豪の皿
× 07/03(金) 25:53~ テレビ東京 アラド:逆転の輪
07/03(金) 25:55~ TBS系 炎炎ノ消防隊 弐ノ章
○- 07/04(土) 22:00~ TOKYO MX Lapis Re:LiGHTs~この世界のアイドルは魔法が使える~
○- 07/04(土) 23:30~ TOKYO MX 魔王学院の不適合者
07/05(日) 25:00~ TOKYO MX 巨人族の花嫁
07/05(日) 25:35~ テレビ東京 八月のシンデレラナイン Re:fine
07/06(月) 21:55~ フジテレビ 超普通都市カシワ伝説R
07/06(月) 24:00~ TOKYO MX THE GOD OF HIGH SCHOOL -ゴッド・オブ・ハイスクール-
07/07(火) 19:00~ Animax ムヒョとロージーの魔法律相談事務所 第2期
× 07/07(火) 21:54~ TOKYO MX うまよん
07/07(火) 24:28~ BS日テレ 戦乙女の食卓
07/07(火) 24:30~ BSフジ 異常生物見聞録
07/07(火) 24:30~ TOKYO MX 放課後ていぼう日誌
× 07/08(水) 23:30~ TOKYO MX Re:ゼロから始める異世界生活 第2期
07/08(水) 24:55~ フジテレビ GREAT PRETENDER
07/08(水) 25:05~ TOKYO MX デカダンス
07/09(木) 25:28~ TBS ノー・ガンズ・ライフ 第2期
07/09(木) 25:58~ TBS やはり俺の青春ラブコメはまちがっている。完
07/09(木) ~ Netflix 日本沈没2020
07/10(金) 22:30~ TOKYO MX 宇崎ちゃんは遊びたい!
07/10(金) 25:25~ TBS系 彼女、お借りします
07/10(金) 25:35~ TOKYO MX ピーター・グリルと賢者の時間
× 07/10(金) 25:50~ TBS系 GETUP! GETLIVE! ショートアニメ
07/11(土) 24:00~ TOKYO MX ソードアート・オンライン-アリシゼーション- War of Underworld -THE LAST SEASON-
07/12(日) 22:30~ TOKYO MX A.I.C.O. -Incarnation-
07/12(日) 23:00~ TOKYO MX モンスター娘のお医者さん
07/12(日) 27:05 ~ テレビ東京 忍者コレクション
07/15(水) 24:00~ TOKYO MX 恋とプロデューサー~EVOL×LOVE~
07/15(水) 22:00~ TOKYO MX GIBIATE the Animation
07/16(木) 24:55~ フジテレビ 富豪刑事 Balance:UNLIMITED

いくつか良さそうなのもあったのだけどその後気に入らなくてガンガン切っていったら現在10本くらい。宇崎ちゃんとかはもう少し我慢して見れば良かったかなぁ。

今のところ一番楽しみにしているのは放課後ていぼう日誌。導入は違和感あったけれど3話~4話くらいから面白さが分かってきた。

2020-06-24 , ,

月は出ているか? Emacs(calendar,org-mode)で月の出・月の入りの時刻を表示する

前回の続き。無事に主要な四月相(新月、上弦の月、満月、下弦の月)の日時を表示できましたが月の出や月の入り、月齢を表示する機能がありません。

月の出・月の入りなんてどうでもいいと思われる方も多いでしょうが、月が出ていない夜は サテライトキャノンが撃てません 月明かりに邪魔されず星がよく見えます。天の川など綺麗な星を見るというのは私の旅行の目的の一つになっていて、特に山へ行く計画を立てるときには必ずチェックしています。月が出ていても時間帯が昼間までならば問題なく星は見られます。カレンダーや予定表を見たときに夜月が明るくない日が分かるとその日に旅行をスケジュールしたくなるわけです。まぁ、月が出ていてもそれはそれで綺麗なのですけれど。いずれにせよ事前に分かっているに越したことはないわけです。

Emacsで日の出日の入りや月の朔望の計算はあるのですから既にありそうなものですが少し探しただけでは見つかりませんでした。

というわけで、作ったのがこちら。

使い方

calendarと連携させるには、例えば次のコードをinit.el(.emacs)に追加します(Mキーはlunar.elで使っているのでLキーにしました)。

(with-eval-after-load "calendar"
  (require 'moonrise)
  (define-key calendar-mode-map "Ld" 'calendar-moonrise-moonset)
  (define-key calendar-mode-map "Lm" 'calendar-moonrise-moonset-month))

L d でカーソル位置の日の時刻をミニバッファに表示し

ミニバッファに月の出、南中、月の入の時刻、月齢を表示
図1: ミニバッファに月の出、南中、月の入の時刻、月齢を表示

L m で月間の時刻を表示します。

一ヶ月分の月の出、南中、月の入の時刻、月齢をまとめて表示
図2: 一ヶ月分の月の出、南中、月の入の時刻、月齢をまとめて表示

また、org-modeのアジェンダに表示するには次の文をアジェンダ表示対象のファイルに追加すればOKです(新しいorgファイルを作った方がいいかも?)。

#+CATEGORY: Astro
* Moonrise
%%(moonrise-org-agenda)
org-modeのagendaに月の出、月の入の時刻を表示
図1: org-modeのagendaに月の出、月の入の時刻を表示

実装にあたって

かなり昔に買ってあった日の出・日の入りの計算―天体の出没時刻の求め方(長沢 工. 1999)という本を元に作成しました。月の位置の計算方法は「海上保安庁が開発した月位置計算の略算式に対し, 時刻原点を変更するなど, 私が多少改変した式」(長沢1999. p124)だそうです。もう20年以上前の本なので精度が気になりましたが海上保安庁水路部の略算式 - 月の位置の略算(1) 黄経: セッピーナの趣味の天文計算によれば今回のような用途に使う分にはまったく問題ない精度のようです。(私も本からの入力に難儀しました。すでに自炊した本だったのでOCRがかかっていたのですが表の部分は全然ダメ。手入力した後音声読み上げさせて目と耳で確認しました)

solar.elとかなりの部分を共通化できるのではないかと思ったのですが、太陽は黄緯が0なので黄経だけ求めればよく、また、時刻の扱い方や計算方法など色々異なっていたためほとんど共通で使えるものはありませんでした。使えたのは、恒星時の計算(solar-sidereal-time)と自転遅れの補正値(solar-ephemeris-correction)、時刻の文字列化(solar-time-string)、それと現在位置を設定する仕組み(solar-setup, calendar-longitude, calendar-latitude, calendar-time-zone)あたりでした。

課題

  • 計算結果のキャッシュ
  • おそらく正しく動作しない地点がある(極地など)

私のマシンではそれほど遅くないのですが、計算結果をキャッシュすると待たされる時間が減るかもしれません。とはいえagendaの生成自体が元々遅いのでどこまで意味があるのか分かりませんが。

極地など地球上のどこかの地点では正しく動作しないと思われます。対処法は本にも書いてあるのですが、面倒なのであまり読んでませんすみません。

(2021-07-25追記)月の満ち欠けを画像表示させてみました

2020-06-21 , ,

月相をEmacsのカレンダーに追加する

きっかけは夏至。今日は夏至かぁ……と思ってorg-modeのアジェンダやcalendarを見ても夏至(Summer Solstice)が表示されていません。Emacsでは標準で表示するはずなのに。調べてみたところ、japanese-holidays.el 用の設定で変数 calendar-holidays の値を日本の休日で完全に置き換えてしまっていました。その際に元々あった (solar-equinoxes-solstices) が消えていたんですね。 calendar-holidays(solar-equinoxes-solstices) を追加したら表示されるようになりました。ついでに英語だとなんだかしっくりこないので solar-n-hemi-seasons("春分" "夏至" "秋分" "冬至") に修正しました。

ついでなので月相も追加してしまおうと思いました。変数 calendar-holidays の説明に次のように書いてあったからです。

To include the phases of the moon, add

(lunar-phases)

to the holiday list, …

M-x describe-function lunar-phases してみるとそういう名前の関数はあるみたいです。なので calendar-holidays(lunar-phases) を追加して M-x calendar してみたところエラー(Wrong type argument: listp, 109)と共に直近の主要な月相一覧が表示されました。 lunar-phases を詳しく調べてみるとこの関数は単に新月、上弦の月、満月、下弦の月の日時を三ヶ月分表示するものだったようです。

再度変数 calendar-holidays の説明をよく見ると続きがあって

To include the phases of the moon, add

(lunar-phases)

to the holiday list, where ‘lunar-phases’ is an Emacs-Lisp function that you’ve written to return a (possibly empty) list of the relevant VISIBLE dates with descriptive strings such as

(((2 6 1989) "New Moon") ((2 12 1989) "First Quarter Moon") … ).

と書いてありました。要するに lunar-phases というのはあくまで例として挙げた架空の関数で、あなたが書いてくださいよ、ということのようです。既にないか探したのですが見つからなかったので仕方なく自分で書くことにしました。

(require 'lunar)
(require 'cl)

(defun my-lunar-phases-for-calendar-holidays ()
  (let ((m displayed-month)
        (y displayed-year))
    (calendar-increment-month m y -1)
    (loop for x in (lunar-phase-list m y)
          collect (list
                   (nth 0 x)
                   (concat
                    (lunar-phase-name (nth 2 x))
                    " "
                    (nth 1 x))))))

(lunar-phase-list) がほぼそのまま使えるかと期待したのですが微妙に形式が違うので変換する必要がありました。

それで、この関数を指定するリスト '(my-lunar-phases-for-calendar-holidays) を変数 calendar-holidays に追加します。 japanese-holidays.el を使っていたりするとすでに書き替えている場所があるかもしれないのでうまいこと追加してください。

lunar-phase-names も日本語にしてしまいましょう。 ("新月" "上弦の月" "満月" "下弦の月")

というわけでなんとか主要な月相をcalendarやorg-modeのアジェンダに表示できました。

2020-06-21-lunar-phases.png

そういえば今日は部分日食が話題になっていました。なるほど月は太陽の方向にあるから新月ですね。

それにしても何で標準で用意されていないのでしょうね。ここまで用意しておきながら。Emacs上で月相を知りたい人は居ないのかな。……居ないんでしょうね。

再度調べてみると次のような質問を見つけました。

org-agendaに直接月相を表示する方法です。calendarに入れなくてもいいならこれでも可。

ところで月の出と月の入りも表示できると便利な気もするのですが……

(2020-06-24追記):次の記事では毎日の月の出と月の入り、南中の時刻、そして毎日の月齢も実現します。

2020-06-03 ,

org-modeで幅の広い表を折り返しモード時でも折り返さず水平スクロールさせる

昨日の続き。

org-mode ではデフォルトで truncate-lines がtとなっています(org-startup-truncated で変更できます) 。つまり、折り返さずに表示するのがデフォルトです。これはおそらく折り返すと幅が広い表(テーブル)が正しく表示されないためではないでしょうか。

しかしそれだと普通のテキストも折り返さずに表示されて大変読みづらくなってしまいます。fill-paragraph 等を使って自分で適当な位置に改行を入れていくことになるのですが多少問題もあります。私はよくHTMLでエクスポートするのですが、文字と文字との間にわずかに空白が入ってしまうことがあります。確認してみるとそれは文章の途中にある改行のせいだったりします。なので私は必要に応じて M-x toggle-truncate-lines で切り替えていたのですが、やっぱり面倒くさい。

というわけで、先日作った部分的に水平スクロールできるようにするコードをorg-modeの表部分に適用してみました。

ソースコードはGitHubにあります。

misohena/phscroll: Enable partial horizontal scroll in Emacs

使い方は (load "org-phscroll.el") するだけ。スクロールは C-x < や C-x > でも可能です。

それと truncate-lines はnilにしないと意味がありません。私は org-startup-truncated をnilに変更してしまいました。

表全体を消した場合自動的にスクロールエリアも削除されますが、表が表で無くなった場合(先頭の|を消すなどして)、スクロールエリアが残ってしまいます。 M-x phscroll-delete-at でポイントがある位置のスクロールエリアを削除できます。

org-phscroll.elの使用例
図1: org-phscroll.elの使用例

実装にあたっては、例によってfont-lockのタイミングで表を探して水平スクロール領域を適用しています。他のタイミングでも良いのですがfont-lock処理の直後、特にorg-fontify-meta-lines-and-blocksの直後ならばfaceがorg-tableかどうかで表かどうかを判別できるのでそこにしました。単純に行頭に|があるかどうかで判別すると、各種ブロック(#+BEGIN_~#+END_)の中にあるものまで拾ってしまうので。

phscroll.el自体の問題も色々修正したのでそこそこ使えるようになってきましたが、大きな表は少し重いかもしれません。できるだけウィンドウ内だけ更新するように作ってありますが、折りたたんだ場所に沢山の表が入っている場所がウィンドウ内に入ると、それらの表を全て一度に整形することになるので時間がかかります。

今のところオーバーレイやテキストプロパティを一文字毎にチェックしているので、それをある程度まとめてやれば少し速くなるかもしれません。面倒くさいですけど。

2020-06-02

Emacsで部分的な水平スクロールを実現する

Emacsでは toggle-truncate-lines を使えばバッファ全体の水平スクロールが可能です。行の折り返しをやめて左右端で切り詰めて表示できるので、幅の長い整形済みテキストを折り返さずに見たい場合には有効です。

しかしこのモードを使うと当然ながら文書全体で折り返しが無くなってしまいます。文書内の通常の文章は折り返して表示し、整形済みの部分だけ水平スクロールを適用する、そんな器用な機能は見当たりませんでした。

一方Emacsにはテキストプロパティオーバーレイという機能でテキストの一部を非表示にできます。となると、行の先頭部分を非表示にすることで擬似的に部分的な水平スクロールを実現できないでしょうか。

というわけで実装したのがこちら:

misohena/phscroll: Enable partial horizontal scroll in Emacs

使い方は

  1. elispをロードする 例:(require 'phscroll)
  2. スクロールさせたいリージョンで囲む
  3. M-x phscroll-region

これで選択した部分だけ水平スクロール出来るようになります。

ただし、truncate-lines が t (折り返さず表示するモード)のときは加工せずそのまま表示します。Emacs標準の水平スクロール機能を使用してください。 M-x toggle-truncate-lines で切り替えると、それを検出して表示を更新します。

既知の問題:

  • 遅い : すでに適用されているテキストプロパティやオーバーレイを調べて各行の幅を計算する部分やオーバーレイを適用して一部を非表示にする部分がかなり遅いみたいです。頑張って必要な部分だけ(windowで表示する範囲内や編集した部分だけ)更新するようにしてみましたが、それでもまだ遅いみたいです。
  • 複数ウィンドウ対応 : 複数のウィンドウで水平スクロール部分を見たときに表示が乱れることがあります。特にフレームを左右に分割したときに左右の幅が異なる場合は選択している方のウィンドウ幅でレイアウト計算するので、他のウィンドウではウィンドウの幅を超えてしまうことがあります。
  • display, invisibleプロパティの対応形式: 既にdisplayプロパティinvisibleプロパティで加工されているテキストも水平スクロールの対象にできますが、正しく幅を計算できる形式は限られています。単純に文字列に置き換えている場合やただ消している場合、それとrelative-spaceは対応しましたがそれ以外は未対応です。
phscroll.el使用例
図1: phscroll.el使用例

これだけだといちいちスクロールさせるエリアを手動で設定しなければならず面倒なので、次回は自動的にエリアを設定する方法について書こうと思います。

ふと思ったのですが、これとは逆に通常部分のみ折り返すというのは可能なのでしょうか。truncate-lines tで通常部分だけオーバーレイで折り返す。オーバーレイで改行ってできるのかな。でも変更があるたびに改行位置を維持するのは大変そうですね。

2020-05-26 ,

org-modeのHTMLエクスポート時にimgタグのalt属性をcaptionからつける

org-mode文書をHTMLでエクスポートしたとき、imgタグのalt=属性(代替テキスト)はデフォルトで画像のファイル名になります。

<img src="./suica.jpg" alt="suica.jpg" />

altがファイル名というのはなんとも気が利かない感じがします。 altを任意の文字列にする方法は Images and XHTML export に書かれていて、画像リンクの前に #+ATTR_HTML: :alt 文字列 と書きます。

#+ATTR_HTML: :alt おいしそうなすいか
[[file:./suica.jpg]]

一方画像にはキャプションを付けることが可能で、画像リンクの前に #+CAPTION: 文字列 と書くと画像の下に「図1:文字列」のようなキャプションがつきます。

#+CAPTION: おいしそうなすいか
#+ATTR_HTML: :alt おいしそうなすいか
[[file:./suica.jpg]]

キャプションには大抵は画像の内容を要約した文が書かれますから、altもキャプションと同じで良いのではないでしょうか? キャプションがあるのだからそもそも代替テキストは不要なのではないか(画像が表示されないときはキャプションを読めば良いのではないか)という気がしなくもありませんが、一応つけるとしたらキャプションと同じ内容で十分なことは多いでしょう。

#+CAPTION:#+ATTR_HTML: の両方を書くのは面倒ですしミスも発生します。私は先に #+CAPTION: だけ使って文書を書き上げてから正規表現置換で #+ATTR_HTML: を生成していたのですが、生成後に #+CAPTION: を修正したときに #+ATTR_HTML: を修正し忘れることが何度もありました。

というわけでエクスポート時にaltを #+CAPTION: から決めるようにするのが次のコードです。

(defun org-altcaption--get-caption (paragraph info)
  "段落に設定されているキャプション文字列を取得する。

org-html-paragraph関数内の「;; Standalone image.」の部分より。"
  (if paragraph
      (let ((raw (org-export-data (org-export-get-caption paragraph) info)))
        (if (org-string-nw-p raw) raw nil))))

(defun org-altcaption--get-link-parent (link info)
"linkの親要素を取得する。ただし、linkが最初のリンクでない場合はnil。

org-html-link関数内より。"
  ;; Extract caption from parent's paragraph.  HACK: Only
  ;; do this for the first link in parent (inner image link
  ;; for inline images).  This is needed as long as
  ;; attributes cannot be set on a per link basis.
  (let* ((parent (org-export-get-parent-element link))
         (link (let ((container (org-export-get-parent link)))
                 (if (and (eq 'link (org-element-type container))
                          (org-html-inline-image-p link info))
                     container
                   link))))
    (and (eq link (org-element-map parent 'link #'identity info t))
         parent)))

(defvar org-altcaption--link nil)

(defun org-altcaption--org-html-link (old-func link desc info)
  "org-html-linkに対するaround advice"
  ;; Pass link to org-altcaption--org-html--format-image function
  (let ((org-altcaption--link link))
    (funcall old-func link desc info)))

(defun org-altcaption--org-html--format-image (old-func source attributes info)
  "org-html--format-imageに対するaround advice"
  ;; Add alt attribute if link has caption
  (if (and org-altcaption--link (null (plist-get attributes :alt)))
      (let ((caption (org-altcaption--get-caption (org-altcaption--get-link-parent org-altcaption--link info) info)))
        (when caption
          (setq attributes (plist-put attributes :alt caption))
          ;;(message "caption=%s" caption)
          )))
  ;; Call original function
  (funcall old-func source attributes info))


(defun org-altcaption-activate ()
  (interactive)
  (advice-add #'org-html-link :around #'org-altcaption--org-html-link)
  (advice-add #'org-html--format-image :around #'org-altcaption--org-html--format-image))

(defun org-altcaption-deactivate ()
  (interactive)
  (advice-remove #'org-html-link #'org-altcaption--org-html-link)
  (advice-remove #'org-html--format-image #'org-altcaption--org-html--format-image))

org-altcaption-activate で有効化、 org-altcaption-deactivate で無効化します。adviceを使って既存の関数をフックしているのでorgのバージョンが上がると動かなくなるかもしれません(9.3.6で確認)。

こんなこと簡単に実現できるだろう、と思いきや結構難しいんですこれが。Orgの文法のうまくできていないところに片足を突っ込んでいる感じです。

Orgでは個別のリンク一つ一つにプロパティを指定することが困難です。例えば段落中のリンクに target="_blank" 属性を設定したい(リンク先を別のウィンドウで開きたい)場合次のように書くのですが

#+ATTR_HTML: :target _blank
山といえば [[https://www.pref.yamanashi.jp/][山梨県]] と [[http://www.pref.shizuoka.jp/index.html][静岡県]] 。富士山にまたがるこの二つの県は……

この場合最初のリンク(山梨県)にしか target="_blank" は設定されません。次のように書くと当然二つの段落に分かれてしまいます。

#+ATTR_HTML: :target _blank
山といえば [[https://www.pref.yamanashi.jp/][山梨県]] と
#+ATTR_HTML: :target _blank
[[http://www.pref.shizuoka.jp/index.html][静岡県]] 。富士山にまたがるこの二つの県は……

属性はリンクに設定されるのではなく段落に対して設定されます。リンクをエクスポートするときは、リンクを包む親要素(段落)に対する属性指定を調べてそれを適用します。属性はリンクについているのではなく親要素についています。これはキャプションも同じで、親要素を調べなければキャプション文字列は分かりません。さらに属性が適用されるのは段落中の最初のリンクのみ。二つ目以降のリンクには適用されない仕様です。キャプションの場合はスタンドアロンな画像(行内にある画像ではなくブロックを形成する画像)にしかキャプションは付けられない仕様なのであまり気にする必要は無いのかもしれませんが、いちおう二つ目の画像リンクには適用しない方がいいでしょう。

ということをするのが org-altcaption--get-link-parent 関数になります。このロジックは org-html-link 関数(ox-html.el)の中にあるものを拝借しました。

リンクそのものにプロパティを指定する文法の提案をどこかで見かけたような気がするのですがうろ覚え。

おいしそうなすいか
図1: おいしそうなすいか
2020-05-26 ,

org-modeのインライン画像のサイズを制限する(最大サイズを指定する)

前回の続きでorg-modeのインライン画像の気に入らないところその2。画像の最大サイズを指定できないところ。

Org文書中からEmacsのフレームよりも大きい画像へリンクを張ることがあります(特にOrg2Blogを使っていると画像はサーバ側で様々なバリエーションを自動生成するので元画像は大きめになります)。そんなときに org-toggle-inline-images でインライン画像を表示するとフレームから画像がはみ出して目も当てられません。

フレームからはみ出す画像
図1: フレームからはみ出す画像

org-modeには org-image-actual-width という変数があり、ImageMagickが有効なときは指定したサイズへスケーリングできます。しかし全ての画像が指定したサイズで表示されてしまうため、小さな画像も大きく拡大されてしまいます。 #+ATTR_HTML: :width 300 のような属性指定を元にサイズを決める機能もありますが、Emacs内での表示のためだけにこの指定をあちこちに入れるのは面倒です。それに用途によってはエクスポートしたときの属性値はEmacs内での表示サイズとは別のものが必要になるでしょう(追記:そういう用途には #+ATTR_ORG: :width を併用するようです> How do I re-scale inline org-mode images to specific widths? - Stack Overflow)。

あくまで欲しいのは可能な限り元のサイズで表示し、設定した最大サイズを超えた場合のみ縮小して表示する機能です。

org-modeにこの機能が無いのはおそらくEmacsのcreate-image関数にそれをサポートする機能が無いためではないでしょうか。create-image関数は幅を指定して読み込む機能(:widthプロパティ)はありますが指定された幅より大きい場合にのみ縮小するような機能(例えば:max-widthとか)はありません(max-image-sizeという変数はありますが、これは読み込む最大のサイズを指定します。それを超えた画像は読み込まれません!)。

幸い画像のサイズを取得するimage-size関数はあるので自分でサイズを計算して読み込むことは可能です。ただしimage-size関数がどのように実装されているかは確認していません。ヘッダーだけ読み込んでサイズを返すならばそれなりに速い動作が期待できますが、画像全体を読み込んでからサイズを返すようだと二度手間になるので速度は低下することでしょう。

それは覚悟の上で最大サイズを指定できるようにするのが次のコードです。

訂正: ImageMagickを使った読み込みでは :max-width、:max-heightが指定できました!(ImageMagick Images - GNU Emacs Lisp Reference Manual) それらを使用して最大サイズを指定できるようにするのが次のコードです。

(defcustom org-limit-image-size '(0.99 . 0.5) "Maximum image size") ;; integer or float or (width-int-or-float . height-int-or-float)

(defun org-limit-image-size--get-limit-size (width-p)
  (let ((limit-size (if (numberp org-limit-image-size)
                        org-limit-image-size
                      (if width-p (car org-limit-image-size)
                        (cdr org-limit-image-size)))))
    (if (floatp limit-size)
        (ceiling (* limit-size (if width-p (frame-text-width) (frame-text-height))))
      limit-size)))

(defvar org-limit-image-size--in-org-display-inline-images nil)

(defun org-limit-image-size--create-image
    (old-func file-or-data &optional type data-p &rest props)

  (if (and org-limit-image-size--in-org-display-inline-images
           org-limit-image-size
           (null type)
           ;;(image-type-available-p 'imagemagick) ;;Emacs27 support scaling by default?
           (null (plist-get props :width)))
      ;; limit to maximum size
      (apply
       old-func
       file-or-data
       (if (image-type-available-p 'imagemagick) 'imagemagick)
       data-p
       (plist-put
        (plist-put
         (org-plist-delete props :width) ;;remove (:width nil)
         :max-width (org-limit-image-size--get-limit-size t))
        :max-height (org-limit-image-size--get-limit-size nil)))

    ;; default
    (apply old-func file-or-data type data-p props)))

(defun org-limit-image-size--org-display-inline-images (old-func &rest args)
  (let ((org-limit-image-size--in-org-display-inline-images t))
    (apply old-func args)))

(defun org-limit-image-size-activate ()
  (interactive)
  (advice-add #'create-image :around #'org-limit-image-size--create-image)
  (advice-add #'org-display-inline-images :around #'org-limit-image-size--org-display-inline-images))

(defun org-limit-image-size-deactivate ()
  (interactive)
  (advice-remove #'create-image #'org-limit-image-size--create-image)
  (advice-remove #'org-display-inline-images #'org-limit-image-size--org-display-inline-images))

org-limit-image-size-activate で有効化、 org-limit-image-size-deactivate で無効化します。adviceを使って既存の関数をフックしているのでorgのバージョンが上がると動かなくなるかもしれません(9.3.6で確認)。

変数 org-limit-image-size には最大サイズを指定します。一つの数値で指定した場合は幅と高さの最大値は同じになります。ドット対 (width . height) の形で二つの数値を指定した場合は幅と高さの最大値は別々になります。数値は整数の場合はピクセル数となります。浮動小数点数の場合はフレームのサイズに対する比率となります。デフォルトは (0.99 . 0.5) で、幅の最大値はほぼフレーム一杯、高さの最大値はフレームの半分くらいまでとしています。

実装にあたっては org-display-inline-images 関数の途中をいじりたかったのですがうまく分割できないので create-image をフックして org-display-inline-images 経由で呼び出されたときだけ動作を変えています。

正直このくらいのことはorg-mode標準で対応して欲しいなぁという気もします。やってることは結局create-imageに:max-widthと:max-heightを付加することだけなので。フックするためのコードが馬鹿らしいですよね。ハマリどころとしては :width nil はダメってことです。Emacs27は分かりませんが、ImageMagickでは :width nil で :max-widthを指定すると画像が出ません。不要な:widthプロパティは削除する必要があります。

追記: Emacs27対応について。Emacs27ではImageMagick対応がデフォルト無効になるそうです。その代わりスケーリングは標準対応になるとNEWS.27に書いてあります(Windowsでも対応するのか不安……)。image.cやdisplay.texiを見る限り:max-widthや:max-heightも標準対応していそう。なのでimagemagickの有無を判定して処理を変えている部分を少し修正して、無くても:max-width、:max-heightプロパティを付加するようにしてみました。ちゃんと対応するなら新しく追加される関数image-transform-pを使用して判別した方が良いかもしれません。org-mode側ではImageMagickの有無で:widthプロパティを設定するかどうかを判別しているので、org-image-actual-widthや#+ATTR_*でのサイズ指定は今のところImageMagick必須です。