reddit / emacs

[mu4e] How to export an email and thread to PDF?

ndpian · 2022-05-30 17:41
I'm trying to find a working solution which allows me to export any email or the entire thread to a nicely formatted pdf file. Is there any package which does this? Currently, in mu4e-action I see the following options: [x] widget view, [V] view in browser, [c] capture message, [v] view in browser, [s] show this thread. Thanks!!
aimebertrand · 2022-05-30 19:21
Hey, I got this in my config: (defun timu-func-mu4e-action-save-to-pdf (msg) "Save mu4e MSG as a pdf. The `wkhtmltopdf' cli command is a dependency. Credit: https://github.com/malb/emacs.d/blob/master/malb.org." (let* ((date (mu4e-message-field msg :date)) (infile (mu4e~write-body-to-html msg)) (outfile (format-time-string "%Y-%m-%d%H%M%S.pdf" date))) (with-temp-buffer (shell-command (format "wkhtmltopdf %s ~/Desktop/%s" infile outfile) t)))) (add-to-list 'mu4e-view-actions '("pdf" . timu-func-mu4e-action-save-to-pdf) t)
ndpian · 2022-05-30 19:53
Thanks for the snippet! However for some reason I get : "Symbol's function definition is void: mu4e~write-body-to-html"
7890yuiop · 2022-05-30 21:16
Probably a different version of `mu4e`. Search in `mu4e-actions.el` for something similar.
aimebertrand · 2022-05-30 21:55
Right. Sorry. The particular Xwidget support in mu/mu4e had been removed in some versions. The following PR tries to reimplement it: [https://github.com/djcb/mu/pull/2077](https://github.com/djcb/mu/pull/2077) What I did is add the file in the PR to my `load-path.` This provides the missing function `mu4e~write-body-to-html` among other needed.
Michaelmrose · 2022-05-31 01:02
Neat work.
ndpian · 2022-05-31 13:57
amazing thanks! yes after loading the files in the PR your script works perfectly! :)
abougouffa · 2022-12-17 16:08
I have a hack in my config which addresses this, the problem with the majority of the examples I found is that they only work with mails with `text/html` part, and for plain-text mails. And most of them do not work properly with Unicode (or non ASCII) mails. I have a hack in [my config](https://github.com/abougouffa/minemacs/blob/ce63b87105e4d144dad5241e2bb048868b40945b/modules/extras/me-mu4e-extras.el#L186-L226) which works for both cases, it depends on `wkhtmltopdf` and `txt2html`. Here it is: ```elisp (defun +clean-file-name (filename &optional downcase-p) "Clean file name." ;; Clean slashes, backslashes, ":", ";", spaces, and tabs (replace-regexp-in-string "[:;\t\n\r /\\_]+" "-" (replace-regexp-in-string "[‘’‚’“”„”\"'()]+" "" (if downcase-p (downcase filename) filename)))) (defun +html2pdf (infile outfile) "Convert HTML file INFILE to PDF and save it to OUTFILE." (call-process "wkhtmltopdf" nil nil nil "--images" "--disable-javascript" "--enable-local-file-access" "--encoding" "utf-8" infile outfile)) (defun +txt2html (infile outfile &optional mail-mode-p) "Convert plain-text file INFILE to HTML and save it to OUTFILE. When MAIL-MODE-P is non-nil, --mailmode is passed to \"txt2html\"." (apply #'call-process (append '("txt2html" nil nil nil "-8") (when mail-mode-p '("--mailmode")) (list "--outfile" outfile infile)))) (defvar +save-as-pdf-filename nil "File name to use, if non-nil, for the output file.") (defun +save-as-pdf (infile &optional _) "Save URL as PDF. This function's signature is compatible with `browse-url-browser-function' so it can be used to save HTML pages or emails to PDF." (let ((outfile (or +save-as-pdf-filename (expand-file-name (file-name-with-extension (file-name-base infile) ".pdf") (file-name-directory infile))))) (if (zerop ;; For HTML files, just call `+html2pdf' (if (string= "html" (file-name-extension infile)) (+html2pdf infile outfile) ;; For non-HTML (plain-text) files, convert them to HTML then call `+html2pdf' (let ((tmp-html (make-temp-file "txt2html-" nil ".html"))) (+txt2html infile tmp-html t) (+html2pdf tmp-html outfile)))) (message "Exported PDF to %s" (abbreviate-file-name outfile)) (user-error (if (file-exists-p outfile) "PDF created but with some errors!" "An error occured, cannot create the PDF!"))))) ;; Based on: mu4e-action-view-in-browser (defun +mu4e-view-save-mail-as-pdf (&optional msg skip-headers) "Save current MSG as PDF. If SKIP-HEADERS is set, do not show include message headers." (interactive) (when-let ((msg (or msg (mu4e-message-at-point)))) (with-temp-buffer (insert-file-contents-literally (mu4e-message-readable-path msg) nil nil nil t) (run-hooks 'gnus-article-decode-hook) (let ((header (unless skip-headers (cl-loop for field in '("from" "to" "cc" "date" "subject") when (message-fetch-field field) concat (format "%s: %s\n" (capitalize field) it)))) (parts (mm-dissect-buffer t t))) ;; If singlepart, enforce a list. (when (and (bufferp (car parts)) (stringp (car (mm-handle-type parts)))) (setq parts (list parts))) ;; Process the list ;; First, `+save-as-pdf' is set as browse-url function, ;; and the appropriate file output file name is bound to `+save-as-pdf-filename' (let ((browse-url-browser-function #'+save-as-pdf) (+save-as-pdf-filename (expand-file-name (format "%s_%s.pdf" (format-time-string "%F" (mu4e-message-field msg :date)) (+clean-file-name (or (mu4e-message-field msg :subject) "No subject") t)) mu4e-attachment-dir))) ;; `gnus-article-browse-html-parts' will try to display the text/html part ;; of the message, but as `+save-as-pdf' is used as browse-url function, ;; it will be called with the proper argumets. (unless (gnus-article-browse-html-parts parts header) ;; If the mail doesn't contain a text/html part, we save the plain-text message ;; and then we explicitly use `+save-as-pdf' to save it. (let ((outfile (make-temp-file "plaintext-mail-" nil ".txt"))) (with-temp-file outfile (insert (mu4e-view-message-text msg))) (+save-as-pdf outfile)))) (mm-destroy-parts parts))))) (add-to-list 'mu4e-view-actions '("pdf" . +mu4e-view-save-mail-as-pdf)) ```