Permalink
Fetching contributors…
Cannot retrieve contributors at this time
317 lines (241 sloc) 9.41 KB

用org-mime在org-mode中发送html邮件

Sending html emails from org-mode with org-mime

在org-mode的邮件列表中进行哦一些关于如何用orgmode发送html格式邮件的讨论. 之前用mu4e来实现的方式已经过时了. 你可以用org-mime这个库来代替以前的实现方法. 我试用了一下这个库,发现还存在以下一些缺陷:

  1. 我发现org中的等式并不会渲染成html中的图片,而且org中所链接的文件也不会自动成为邮件的附件. 对此我做了一些修改.
  2. 我发现使用org-mime命令从org buffer中生成email时,若能把光标保留在 To: 的位置就好了.
  3. 我使用mu4e来发送邮件, 因此我创建了一个函数来用 org-mu4e-compose-org-mode 编辑message, 然后为 C-c C-c 添加了一个hook来方便我发送邮件(函数定义参见这里).

这篇博文主要记录了我折腾出来发送html邮件的那些步骤. 经过实验, 上面列出的有些缺陷已经在最新的org-mime中得到了修复.

第一步,你需要require org-mime 这个库.

(require 'org-mime)

你可以通过命令 elisp:org-mime-org-buffer-htmlize 来将整个org buffer作为html邮件来发送. 不过这个命令并不能支持所有的内部链接(at least in gmail).

这个命令默认会把你的光标移动到buffer的末尾位置,这样很不方便. 我们可以稍微修改一下这个函数来让光标定位到 To: 的位置.

(defun org-mime-org-buffer-htmlize ()
  "Create an email buffer containing the current org-mode file
  exported to html and encoded in both html and in org formats as
  mime alternatives."
  (interactive)
  (org-mime-send-buffer 'html)
  (message-goto-to))

From an org-headline in an org-file

你可以将org buffer中的某个heading内容转换为html邮件发送. 这种情况下, 你需要为heading指定 MAIL_FMT 属性为html, 像这样:

:PROPERTIES:
:MAIL_FMT: html
:END:

除此之外,你还可以设置下面这些属性.

	   (subject (or (funcall mp "MAIL_SUBJECT") (nth 4 (org-heading-components))))
	   (to (funcall mp "MAIL_TO"))
	   (cc (funcall mp "MAIL_CC"))
	   (bcc (funcall mp "MAIL_BCC"))

完了后, 通过命令 elisp:org-mime-subtree 来将该heading作为邮件发送出去.

同样的,我也修改了一下这个函数,来让光标停留在 To: 位置.

(defun org-mime-subtree ()
  "Create an email buffer containing the current org-mode subtree
  exported to a org format or to the format specified by the
  MAIL_FMT property of the subtree."
  (interactive)
  (org-mime-send-subtree
   (or (org-entry-get nil "MAIL_FMT" org-mime-use-property-inheritance) 'org))
  (message-goto-to))

下面是一些例子,可以看看这些元素是否能正常的转换为html.

标记

粗体

下划线

斜体

删除线

代码

下标: H_{2}O 上标: H^{+} 实体: To ∞ and beyond

等式

\(x^2\)

\[x^4\]

$e^x$

表格

xy
12

列表

嵌套列表.

  • one
    • Subentry under one.
  • two

定义列表:

def1
first definition

检查列表:

  • [ ] A checkbox

带数字编号的列表:

  1. number 1
  2. number 2

代码块

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(0, 10)
x = np.cos(t) * np.exp(-t)
y = np.sin(t) * np.exp(-t)

plt.plot(x, y)
plt.savefig('spiral.png')

./spiral.png

其他目录中的图片

./images/Au-icosahedron-3.png

org-ref的引用

abc

See Table ref:table-1.

cite:Dominik201408

bibliography:../../../Dropbox/bibliography/references.bib

In a mail message

如果你想直接在邮件里用org的形式编写内容. 你可以用下面的方式适用于mu4e用户. 下面的这个命令会以org-mode打开一个写消息的窗口,而且在邮件header与邮件body之间切换的同时也会切换到不同的mode下. 如果你经常这样做,那么你可以为message-mode加一个hook. 我自己不怎么发送html邮件因此我没有这样做.

(defun mu4e-compose-org-mail ()
 (interactive)
 (mu4e-compose-new)
 (org-mu4e-compose-org-mode))

在发送时,我们需要使用 org-mime 来将org内容转换成html, 因此我们为 C-c C-c 添加一个hook函数来发送邮件. 这个hook函数有一些棘手, 我们需要保持 C-c C-c 在org-mode中的行为不变, 例如,在code block中按下 C-c C-c 要能执行该代码块, 但是当 C-c C-c 不触发其他动作时则发送邮件. 为了做到这一点,你需要将该hook函数放在hook的最末尾才行. 当然,你也可以为该功能分配其他的快捷键,或者直接用 M-x 运行该命令也行. 需要注意的是,这个 C-c C-c hook函数只有在光标处于email的boy时才生效. 当在email的header处按下 C-c C-c 则会将邮件内容以纯文本的方式发送出去.

(defun htmlize-and-send ()
  "When in an org-mu4e-compose-org-mode message, htmlize and send it."
  (interactive)
  (when (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)
    (org-mime-htmlize) 
    (message-send-and-exit)))

(add-hook 'org-ctrl-c-ctrl-c-hook 'htmlize-and-send t)

下面的方法则可以可非mu4e用户做到在直接在邮件里编写org格式的内容,但是它无法做到mode的自动切换. 因此你会失去邮件的补全功能,以及那些与邮件header相关的功能,除非你重新切换回message-mode.

(defun compose-html-org ()
  (interactive)
  (compose-mail)
  (message-goto-body)
  (setq *compose-html-org* t)
  (org-mode))

(defun org-htmlize-and-send ()
  "When in an org-mu4e-compose-org-mode message, htmlize and send it."
  (interactive)
  
  (when *compose-html-org*
    (setq *compose-html-org* nil)
    (message-mode)
    (org-mime-htmlize) 
    (message-send-and-exit)))

(add-hook 'org-ctrl-c-ctrl-c-hook 'org-htmlize-and-send t)

Equations and file attachments do not seem to work out of the box

\(e^{iπ} - 1 = 0\)

不做任何配置的情况下, org-mime似乎并不会将file链接的文件加入到电子邮件的附件中,也不会将内容中的等式转换成图片..

file:html-email.org

下面的 org-mime-compose 能帮你实现这一点.

(defun org-mime-compose (body fmt file &optional to subject headers)
  (require 'message)
  (let ((bhook
	 (lambda (body fmt)
	   (let ((hook (intern (concat "org-mime-pre-"
				       (symbol-name fmt)
				       "-hook"))))
	     (if (> (eval `(length ,hook)) 0)
		 (with-temp-buffer
		   (insert body)
		   (goto-char (point-min))
		   (eval `(run-hooks ',hook))
		   (buffer-string))
	       body))))
	(fmt (if (symbolp fmt) fmt (intern fmt)))
	(files (org-element-map (org-element-parse-buffer) 'link
		 (lambda (link)
		   (when (string= (org-element-property :type link) "file")
		     (file-truename (org-element-property :path link)))))))
    (compose-mail to subject headers nil)
    (message-goto-body)
    (cond
     ((eq fmt 'org)
      (require 'ox-org)
      (insert (org-export-string-as
	       (org-babel-trim (funcall bhook body 'org)) 'org t)))
     ((eq fmt 'ascii)
      (require 'ox-ascii)
      (insert (org-export-string-as
	       (concat "#+Title:\n" (funcall bhook body 'ascii)) 'ascii t)))
     ((or (eq fmt 'html) (eq fmt 'html-ascii))
      (require 'ox-ascii)
      (require 'ox-org)
      (let* ((org-link-file-path-type 'absolute)
	     ;; we probably don't want to export a huge style file
	     (org-export-htmlize-output-type 'inline-css)
	     (org-html-with-latex 'dvipng)
	     (html-and-images
	      (org-mime-replace-images
	       (org-export-string-as (funcall bhook body 'html) 'html t)))
	     (images (cdr html-and-images))
	     (html (org-mime-apply-html-hook (car html-and-images))))
	(insert (org-mime-multipart
		 (org-export-string-as
		  (org-babel-trim
		   (funcall bhook body (if (eq fmt 'html) 'org 'ascii)))
		  (if (eq fmt 'html) 'org 'ascii) t)
		 html)
		(mapconcat 'identity images "\n")))))
    (mapc #'mml-attach-file files)))

Summary

用这种方法来发送具有丰富格式的html邮件真是太棒了.