Yearly Archives: 2020

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必須です。

2020-05-25 ,

org-modeのインライン画像をリアルタイムで更新する

(追記: https://github.com/misohena/org-inline-image-fix/blob/master/org-flyimage.el にソースコードを上げました。org-flyimage-modeというマイナーモードにしてあります)

org-modeには画像へのリンクをインライン表示する機能(org-toggle-inline-images (C-c C-x C-v))がありますが、私はたまにしか使っていませんでした。

理由は色々あるのですが、その一つが画像を瞬時に更新してくれないこと。画像のへのリンクを修正したり追加したりしても自動的に画像を更新してくれません。コメントアウトしても表示されたまま。org-redisplay-inline-images (C-c C-x C-M-v)で更新できるのですが、これはいったん全ての画像を消してから再表示するため画像が多いととても時間がかかります。それだといっそのこと画像なんて表示せずに編集して、まとまったところでhtmlにでもエクスポートして確認すれば済んでしまいます。

というわけで編集するそばから自動的に画像を更新させるのが次のコードです。

(defvar org-flyimage-in-activate-links nil)

(defun org-flyimage-activate-links (old-func limit)
  (let ((org-flyimage-in-activate-links t))
    (funcall old-func limit)))

(defun org-flyimage-remove-inline-images-in (beg end)
  ;; is faster to use (overlays-in beg end) ?
  (loop for ov in org-inline-image-overlays
        if (let ((ovbeg (overlay-start ov))
                 (ovend (overlay-end ov)))
             (and (numberp ovbeg) (numberp ovend)
                  (< ovbeg end) (> ovend beg)))
        do (org-display-inline-remove-overlay ov t nil nil)))

(defun org-flyimage-remove-flyspell-overlays-in (old-func beg end)
  (funcall old-func beg end)

  (if t ;;(not org-flyimage-in-activate-links) ;;in t, reflect #+attr_html: :width immediately. but slow
      (org-flyimage-remove-inline-images-in beg end))

  (if org-flyimage-in-activate-links
      (org-display-inline-images nil t beg end)))

(defun org-flyimage-activate ()
  (interactive)
  (advice-add #'org-activate-links :around #'org-flyimage-activate-links)
  (advice-add #'org-remove-flyspell-overlays-in :around #'org-flyimage-remove-flyspell-overlays-in))

(defun org-flyimage-deactivate ()
  (interactive)
  (advice-remove #'org-activate-links #'org-flyimage-activate-links)
  (advice-remove #'org-remove-flyspell-overlays-in #'org-flyimage-remove-flyspell-overlays-in))

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

仕組みとしてはfont-lockのタイミングでインライン画像用のオーバーレイを作成・削除しています。

org-modeでは、リンクのハイライトは org-activate-links で行われています。この関数の中ではリンクの種別が判定されてリンク文字列の範囲に様々なテキストプロパティが設定されます。

;; org.elより
(defun org-activate-links (limit)
  "Add link properties to links.
This includes angle, plain, and bracket links."
  (catch :exit
    (while (re-search-forward org-link-any-re limit t)
      (let* ((start (match-beginning 0))
             (end (match-end 0))
             (visible-start (or (match-beginning 3) (match-beginning 2)))
             (visible-end (or (match-end 3) (match-end 2)))
             (style (cond ((eq ?< (char-after start)) 'angle)
                          ((eq ?\[ (char-after (1+ start))) 'bracket)
                          (t 'plain))))
        (when (and (memq style org-highlight-links)
                   ;; Do not span over paragraph boundaries.
                   (not (string-match-p org-element-paragraph-separate
                                        (match-string 0)))
                   ;; Do not confuse plain links with tags.
                   (not (and (eq style 'plain)
                             (let ((face (get-text-property
                                          (max (1- start) (point-min)) 'face)))
                               (if (consp face) (memq 'org-tag face)
                                 (eq 'org-tag face))))))
          (let* ((link-object (save-excursion
                                (goto-char start)
                                (save-match-data (org-element-link-parser))))
                 (link (org-element-property :raw-link link-object))
                 (type (org-element-property :type link-object))
                 (path (org-element-property :path link-object))
                 (properties            ;for link's visible part
                  (list
                   'face (pcase (org-link-get-parameter type :face)
                           ((and (pred functionp) face) (funcall face path))
                           ((and (pred facep) face) face)
                           ((and (pred consp) face) face) ;anonymous
                           (_ 'org-link))
                   'mouse-face (or (org-link-get-parameter type :mouse-face)
                                   'highlight)
                   'keymap (or (org-link-get-parameter type :keymap)
                               org-mouse-map)
                   'help-echo (pcase (org-link-get-parameter type :help-echo)
                                ((and (pred stringp) echo) echo)
                                ((and (pred functionp) echo) echo)
                                (_ (concat "LINK: " link)))
                   'htmlize-link (pcase (org-link-get-parameter type
                                                                :htmlize-link)
                                   ((and (pred functionp) f) (funcall f))
                                   (_ `(:uri ,link)))
                   'font-lock-multiline t)))
            (org-remove-flyspell-overlays-in start end)
            (org-rear-nonsticky-at end)
            (if (not (eq 'bracket style))
                (add-text-properties start end properties)
              ;; Handle invisible parts in bracket links.
              (remove-text-properties start end '(invisible nil))
              (let ((hidden
                     (append `(invisible
                               ,(or (org-link-get-parameter type :display)
                                    'org-link))
                             properties)))
                (add-text-properties start visible-start hidden)
                (add-text-properties visible-start visible-end properties)
                (add-text-properties visible-end end hidden)
                (org-rear-nonsticky-at visible-start)
                (org-rear-nonsticky-at visible-end)))
            (let ((f (org-link-get-parameter type :activate-func)))
              (when (functionp f)
                (funcall f start end path (eq style 'bracket))))
            (throw :exit t)))))         ;signal success
    nil))

この org-activate-links を修正してテキストプロパティを設定するのと同時に画像のオーバーレイも生成しよう、と最初は思ったのですが、それだと消す方はどうするのだろうと疑問が浮かびました。現状でもリンク自体を修正すると画像は消えるようにできています。オーバーレイにmodification-hooksが設定されているので。でもリンク自体を修正せずにリンクでは無くなる場合、例えば画像へのリンクの前に# を入れてコメントアウトするとfaceはリンクの色からコメントの色に変わります。それと一緒に画像も消えて欲しいわけです。テキストプロパティは変わるわけですから、どこかでその処理があるはずです。ここで設定したテキストプロパティを消しているのはどこなんだろう……。

詳しく追うのは大変そうだな……と思ったときにふと目に入ったのが (org-remove-flyspell-overlays-in start end) の部分。これはおそらくflyspellのオーバーレイを削除するためのものでしょう。org.el内を検索するとあちこちで org-remove-flyspell-overlays-in が呼ばれていました。コメントの色づけをすると思わしき場所でも。おそらく範囲(start end)の分類が変わるときにはこの関数を呼び出して表示をリセットするのではないでしょうか。

そこで (org-remove-flyspell-overlays-in start end) をフックしてstartからendの間にあるオーバーレイを削除してみると、無事コメントアウトで画像が削除されました(笑)。

org-activate-links も内部で org-remove-flyspell-overlays-in を呼び出しますから、 org-activate-links ではなく org-remove-flyspell-overlays-in の方で指定された範囲の画像を更新してしまえば良いでしょう。 org-activate-links 経由かどうか判定できるようにして、そのときだけ処理を変えるようにしました。

org-flyimage-remove-flyspell-overlays-in 内のコメントがある部分ですが、tだとリンク更新時に必ずオーバーレイを作り直すので少しだけ遅くなります。ただ、 #+ATTR_HTML: :width で画像サイズを調整するときにリアルタイムで反映される(注意:org-image-actual-widthとimagemagickサポートによる)のでとりあえずこうしてあります。本来は org-display-inline-images の側でサイズの変更を検出して作り直すかどうか判断して欲しいところです。

今回の件とは直接関係ないですが、 org-display-inline-imagesorg-image-actual-width には画像の最大サイズを指定する機能が欲しいです。先に画像サイズを読み込んで計算してから再度画像を読み込まなければならないと思うので速度低下が心配ですが。

font-lock時に画像を読み込むのは速度低下の心配がありますが、極端に大きな画像で無ければ私の所では今のところ問題ないようです。気になるようなら遅延読み込みをした方が良いかもしれません。

大量の画像を整理して不要なものをコメントアウトしたりサイズを調整したりする作業
図1: 大量の画像を整理して不要なものをコメントアウトしたりサイズを調整したりする作業
2020-05-23

NTEmacsのWanderlustでメール送信時に時々SMTP errorが出る問題が解消?

Windows上のEmacsで Wanderlust を使っていて長年悩まされていたのがSMTP error。送信時に SMTP error と出て送信が中断してしまいます。何度か繰り返すと成功するのですが、たまに失敗時にも送れてしまうことがあるらしく何通も相手に送ってしまっている場合があります。

長年の経験で原因はどうも SSL/TLS まわりにあるらしいことが分かっています。SSL/TLSを使わないと発生しないので。Emacs組み込みのgnutlsを使うか外部のコマンドを使うかでも挙動が変わってきます。SSL/TLSを使うと受信時にも時々エラーが発生して何度か繰り返すとうまく行くことがあります。

この問題が解決するならばとGnusへの移行を試してみたこともあるのですが同じ問題が発生したので無意味でした。

今日少しこの問題を調査してみたのですが、結論から言うと

(setq gnutls-log-level 5)

を設定したらSMTP errorが出なくなりました。

な、なんだってー!

このコードはgnutlsのログレベルを上げて沢山のログを出すようにしただけです。

gnutls-log-levelを2にして出たログが次です。

Sending...
gnutls.c: [1] (Emacs) connecting to host: ****.****.ne.jp
gnutls.c: [1] (Emacs) allocating credentials
gnutls.c: [2] (Emacs) allocating x509 credentials
gnutls.c: [2] (Emacs) using default verification flags
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=CORP\\srv-build-cd.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=****.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=****.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=CORP\\srv-build-cd.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: OU=Copyright (c) 1997 Microsoft Corp.,OU=Microsoft Corporation,CN=Microsoft Root Authority.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=****.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: C=US,ST=California,L=Newark,O=Logitech Inc,CN=Logitech Inc.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: C=US,O=MSFT,CN=Microsoft Authenticode(tm) Root Authority.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=CORP\\srv-build-cd.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=CORP\\srv-build-cd.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=****.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=Root Agency.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [audit] There was a non-CA certificate in the trusted list: CN=dsalocal.intel.com.
gnutls.c: [1] (Emacs) setting the trustfile:  c:/****/certs/ca-bundle.crt
gnutls.c: [1] (Emacs) gnutls callbacks
gnutls.c: [1] (Emacs) gnutls_init
gnutls.c: [1] (Emacs) got non-default priority string: NORMAL:%DUMBFW
gnutls.c: [1] (Emacs) setting the priority string
gnutls.c: [2] added 6 protocols, 29 ciphersuites, 18 sig algos and 9 groups into priority list
gnutls.c: [2] Keeping ciphersuite 13.02 (GNUTLS_AES_256_GCM_SHA384)
gnutls.c: [2] Keeping ciphersuite 13.03 (GNUTLS_CHACHA20_POLY1305_SHA256)
gnutls.c: [2] Keeping ciphersuite 13.01 (GNUTLS_AES_128_GCM_SHA256)
gnutls.c: [2] Keeping ciphersuite 13.04 (GNUTLS_AES_128_CCM_SHA256)
gnutls.c: [2] Keeping ciphersuite c0.2c (GNUTLS_ECDHE_ECDSA_AES_256_GCM_SHA384)
gnutls.c: [2] Keeping ciphersuite cc.a9 (GNUTLS_ECDHE_ECDSA_CHACHA20_POLY1305)
gnutls.c: [2] Keeping ciphersuite c0.ad (GNUTLS_ECDHE_ECDSA_AES_256_CCM)
gnutls.c: [2] Keeping ciphersuite c0.0a (GNUTLS_ECDHE_ECDSA_AES_256_CBC_SHA1)
gnutls.c: [2] Keeping ciphersuite c0.2b (GNUTLS_ECDHE_ECDSA_AES_128_GCM_SHA256)
gnutls.c: [2] Keeping ciphersuite c0.ac (GNUTLS_ECDHE_ECDSA_AES_128_CCM)
gnutls.c: [2] Keeping ciphersuite c0.09 (GNUTLS_ECDHE_ECDSA_AES_128_CBC_SHA1)
gnutls.c: [2] Keeping ciphersuite c0.30 (GNUTLS_ECDHE_RSA_AES_256_GCM_SHA384)
gnutls.c: [2] Keeping ciphersuite cc.a8 (GNUTLS_ECDHE_RSA_CHACHA20_POLY1305)
gnutls.c: [2] Keeping ciphersuite c0.14 (GNUTLS_ECDHE_RSA_AES_256_CBC_SHA1)
gnutls.c: [2] Keeping ciphersuite c0.2f (GNUTLS_ECDHE_RSA_AES_128_GCM_SHA256)
gnutls.c: [2] Keeping ciphersuite c0.13 (GNUTLS_ECDHE_RSA_AES_128_CBC_SHA1)
gnutls.c: [2] Keeping ciphersuite 00.9d (GNUTLS_RSA_AES_256_GCM_SHA384)
gnutls.c: [2] Keeping ciphersuite c0.9d (GNUTLS_RSA_AES_256_CCM)
gnutls.c: [2] Keeping ciphersuite 00.35 (GNUTLS_RSA_AES_256_CBC_SHA1)
gnutls.c: [2] Keeping ciphersuite 00.9c (GNUTLS_RSA_AES_128_GCM_SHA256)
gnutls.c: [2] Keeping ciphersuite c0.9c (GNUTLS_RSA_AES_128_CCM)
gnutls.c: [2] Keeping ciphersuite 00.2f (GNUTLS_RSA_AES_128_CBC_SHA1)
gnutls.c: [2] Keeping ciphersuite 00.9f (GNUTLS_DHE_RSA_AES_256_GCM_SHA384)
gnutls.c: [2] Keeping ciphersuite cc.aa (GNUTLS_DHE_RSA_CHACHA20_POLY1305)
gnutls.c: [2] Keeping ciphersuite c0.9f (GNUTLS_DHE_RSA_AES_256_CCM)
gnutls.c: [2] Keeping ciphersuite 00.39 (GNUTLS_DHE_RSA_AES_256_CBC_SHA1)
gnutls.c: [2] Keeping ciphersuite 00.9e (GNUTLS_DHE_RSA_AES_128_GCM_SHA256)
gnutls.c: [2] Keeping ciphersuite c0.9e (GNUTLS_DHE_RSA_AES_128_CCM)
gnutls.c: [2] Keeping ciphersuite 00.33 (GNUTLS_DHE_RSA_AES_128_CBC_SHA1)
gnutls.c: [2] Advertizing version 3.4
gnutls.c: [2] Advertizing version 3.3
gnutls.c: [2] Advertizing version 3.2
gnutls.c: [2] Advertizing version 3.1
gnutls.c: [2] HSK[0000000005c5d140]: sent server name: '****.****.ne.jp'
gnutls.c: [1] (Emacs) non-fatal error: Resource temporarily unavailable, try again. [73 times]
gnutls.c: [audit] FFDHE groups advertised, but server didn't support it; falling back to server's choice
gnutls.c: [1] (Emacs) non-fatal error: Resource temporarily unavailable, try again. [295 times]
gnutls.c: [2] (Emacs) Deallocating x509 credentials
Invalid response: ... Recipient ok
Invalid response: RCPT TO:<****@****.jp>
wl-draft-send-mail-with-smtp: SMTP error
SMTP error

「non-fatal error: Resource temporarily unavailable, try again.」というのが73回+295回も繰り返されているのが気になります。

このエラーメッセージはgnutlsのGNUTLS_E_AGAINに対応するもののようです。

GNUTLS_E_AGAINはあちこちで発生するようです。

もう少し詳細なログが出ないかgnutls-log-levelを5にして再度試してみたところ、何回繰り返してもエラーが発生しません。

ログを見ると次のように大量のretryメッセージが出ていました。

gnutls.c: [3] ASSERT: ../../../gnutls-3.6.10/lib/nettle/mpi.c[wrap_nettle_mpi_print]:60
gnutls.c: [4] HSK[00000000021cc030]: CLIENT KEY EXCHANGE was queued [262 bytes]
gnutls.c: [4] REC[00000000021cc030]: Sent ChangeCipherSpec
gnutls.c: [5] REC[00000000021cc030]: Initializing epoch #1
gnutls.c: [5] REC[00000000021cc030]: Epoch #1 ready
gnutls.c: [4] HSK[00000000021cc030]: Cipher Suite: GNUTLS_DHE_RSA_AES_256_GCM_SHA384
gnutls.c: [4] HSK[00000000021cc030]: Initializing internal [write] cipher sessions
gnutls.c: [4] HSK[00000000021cc030]: recording tls-unique CB (send)
gnutls.c: [4] HSK[00000000021cc030]: FINISHED was queued [16 bytes]
gnutls.c: [5] REC[00000000021cc030]: Preparing Packet Handshake(22) with length: 7 and min pad: 0
gnutls.c: [5] REC[00000000021cc030]: Sent Packet[2] Handshake(22) in epoch 0 and length: 12
gnutls.c: [5] REC[00000000021cc030]: Preparing Packet Handshake(22) with length: 262 and min pad: 0
gnutls.c: [5] REC[00000000021cc030]: Sent Packet[3] Handshake(22) in epoch 0 and length: 267
gnutls.c: [5] REC[00000000021cc030]: Preparing Packet ChangeCipherSpec(20) with length: 1 and min pad: 0
gnutls.c: [5] REC[00000000021cc030]: Sent Packet[4] ChangeCipherSpec(20) in epoch 0 and length: 6
gnutls.c: [5] REC[00000000021cc030]: Preparing Packet Handshake(22) with length: 16 and min pad: 0
gnutls.c: [5] REC[00000000021cc030]: Sent Packet[1] Handshake(22) in epoch 1 and length: 45
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[get_last_packet]:1168
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[_gnutls_io_read_buffered]:589
gnutls.c: [3] (Emacs) retry: Resource temporarily unavailable, try again.
gnutls.c: [1] (Emacs) non-fatal error: Resource temporarily unavailable, try again.
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[get_last_packet]:1168
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[_gnutls_io_read_buffered]:589
gnutls.c: [3] (Emacs) retry: Resource temporarily unavailable, try again.
gnutls.c: [1] (Emacs) non-fatal error: Resource temporarily unavailable, try again.
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[get_last_packet]:1168
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[_gnutls_io_read_buffered]:589
gnutls.c: [3] (Emacs) retry: Resource temporarily unavailable, try again.
gnutls.c: [1] (Emacs) non-fatal error: Resource temporarily unavailable, try again.
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[get_last_packet]:1168
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[_gnutls_io_read_buffered]:589
gnutls.c: [3] (Emacs) retry: Resource temporarily unavailable, try again.
gnutls.c: [1] (Emacs) non-fatal error: Resource temporarily unavailable, try again.
gnutls.c: [3] ASSERT: ../../gnutls-3.6.10/lib/buffers.c[get_last_packet]:1168
....繰り返し

これは推測なのですが、リトライ時のメッセージ出力量が増えたことによってリトライの間隔が延びたことが影響しているのではないでしょうか。これらのメッセージは逐一ミニバッファに出るので体感できるくらいには遅くなります。

(setq gnutls-log-level 5) を設定したまま1~2日ほど使ってみましたが今のところエラーは発生していません。

これ以上深くは追っていないのですが、またエラーが発生したら調べてみようと思います。

2020-06-21追記:未だにエラーが出ません。