1 ;;; mm-view.el --- functions for viewing MIME objects
2 ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
3 ;; 2004 Free Software Foundation, Inc.
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6 ;; This file is part of GNU Emacs.
8 ;; GNU Emacs is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; GNU Emacs is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with GNU Emacs; see the file COPYING. If not, write to the
20 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 ;; Boston, MA 02111-1307, USA.
27 (eval-when-compile (require 'cl))
34 (autoload 'gnus-article-prepare-display "gnus-art")
35 (autoload 'vcard-parse-string "vcard")
36 (autoload 'vcard-format-string "vcard")
37 (autoload 'fill-flowed "flow-fill")
38 (autoload 'html2text "html2text"))
40 (defvar mm-text-html-renderer-alist
41 '((w3 . mm-inline-text-html-render-with-w3)
42 (w3m . mm-inline-text-html-render-with-w3m)
43 (w3m-standalone mm-inline-render-with-stdin nil
44 "w3m" "-dump" "-T" "text/html")
45 (links mm-inline-render-with-file
46 mm-links-remove-leading-blank
48 (lynx mm-inline-render-with-stdin nil
49 "lynx" "-dump" "-force_html" "-stdin" "-nolist")
50 (html2text mm-inline-render-with-function html2text))
51 "The attributes of renderer types for text/html.")
53 (defvar mm-text-html-washer-alist
54 '((w3 . gnus-article-wash-html-with-w3)
55 (w3m . gnus-article-wash-html-with-w3m)
56 (w3m-standalone mm-inline-wash-with-stdin nil
57 "w3m" "-dump" "-T" "text/html")
58 (links mm-inline-wash-with-file
59 mm-links-remove-leading-blank
61 (lynx mm-inline-wash-with-stdin nil
62 "lynx" "-dump" "-force_html" "-stdin" "-nolist")
63 (html2text html2text))
64 "The attributes of washer types for text/html.")
66 (defcustom mm-fill-flowed t
67 "If non-nil a format=flowed article will be displayed flowed."
71 ;;; Internal variables.
74 ;;; Functions for displaying various formats inline
77 (defun mm-inline-image-emacs (handle)
78 (let ((b (point-marker))
80 (put-image (mm-get-image handle) b)
81 (mm-handle-set-undisplayer
87 (delete-region b (+ b 2)))))))
89 (defun mm-inline-image-xemacs (handle)
92 (let ((annot (make-annotation (mm-get-image handle) nil 'text))
94 (mm-handle-set-undisplayer
97 (let ((b ,(point-marker))
99 (delete-annotation ,annot)
100 (delete-region (- b 2) b))))
101 (set-extent-property annot 'mm t)
102 (set-extent-property annot 'duplicable t)))
105 (if (featurep 'xemacs)
106 (defalias 'mm-inline-image 'mm-inline-image-xemacs)
107 (defalias 'mm-inline-image 'mm-inline-image-emacs)))
109 (defvar mm-w3-setup nil)
110 (defun mm-setup-w3 ()
117 (setq mm-w3-setup t)))
119 (defun mm-inline-text-html-render-with-w3 (handle)
121 (let ((text (mm-get-part handle))
123 (url-standalone-mode t)
124 (url-gateway-unplugged t)
125 (w3-honor-stylesheets nil)
127 (url-generic-parse-url (format "cid:%s" (mm-handle-id handle))))
128 (width (window-width))
129 (charset (mail-content-type-get
130 (mm-handle-type handle) 'charset)))
132 (insert (if charset (mm-decode-string text charset) text))
134 (narrow-to-region b (point))
136 (goto-char (point-min))
137 (when (or (and (boundp 'w3-meta-content-type-charset-regexp)
139 w3-meta-content-type-charset-regexp nil t))
140 (and (boundp 'w3-meta-charset-content-type-regexp)
142 w3-meta-charset-content-type-regexp nil t)))
144 (let ((bsubstr (buffer-substring-no-properties
147 (if (fboundp 'w3-coding-system-for-mime-charset)
148 (w3-coding-system-for-mime-charset bsubstr)
149 (mm-charset-to-coding-system bsubstr))))
150 (delete-region (point-min) (point-max))
151 (insert (mm-decode-string text charset))))
152 (save-window-excursion
154 (let ((w3-strict-width width)
155 ;; Don't let w3 set the global version of
157 (fill-column fill-column))
158 (if (or debug-on-error debug-on-quit)
159 (w3-region (point-min) (point-max))
161 (w3-region (point-min) (point-max))
163 (delete-region (point-min) (point-max))
165 (charset (mail-content-type-get
166 (mm-handle-type handle) 'charset)))
167 (if (or (eq charset 'gnus-decoded)
168 (eq mail-parse-charset 'gnus-decoded))
170 (narrow-to-region (point) (point))
171 (mm-insert-part handle)
172 (goto-char (point-max)))
173 (insert (mm-decode-string (mm-get-part handle)
176 "Error while rendering html; showing as text/plain")))))))
177 (mm-handle-set-undisplayer
180 (let (buffer-read-only)
181 (if (functionp 'remove-specifier)
182 (mapcar (lambda (prop)
184 (face-property 'default prop)
186 '(background background-pixmap foreground)))
187 (delete-region ,(point-min-marker)
188 ,(point-max-marker)))))))))
190 (defvar mm-w3m-setup nil
191 "Whether gnus-article-mode has been setup to use emacs-w3m.")
193 (defun mm-setup-w3m ()
194 "Setup gnus-article-mode to use emacs-w3m."
197 (unless (assq 'gnus-article-mode w3m-cid-retrieve-function-alist)
198 (push (cons 'gnus-article-mode 'mm-w3m-cid-retrieve)
199 w3m-cid-retrieve-function-alist))
200 (setq mm-w3m-setup t))
201 (setq w3m-display-inline-images mm-inline-text-html-with-images))
203 (defun mm-w3m-cid-retrieve-1 (url handle)
204 (if (mm-multiple-handles handle)
205 (dolist (elem handle)
206 (mm-w3m-cid-retrieve-1 url elem))
207 (when (and (listp handle)
208 (equal url (mm-handle-id handle)))
209 (mm-insert-part handle)
210 (throw 'found-handle (mm-handle-media-type handle)))))
212 (defun mm-w3m-cid-retrieve (url &rest args)
213 "Insert a content pointed by URL if it has the cid: scheme."
214 (when (string-match "\\`cid:" url)
216 (mm-w3m-cid-retrieve-1 (concat "<" (substring url (match-end 0)) ">")
217 (with-current-buffer w3m-current-buffer
218 gnus-article-mime-handles)))))
220 (defun mm-inline-text-html-render-with-w3m (handle)
221 "Render a text/html part using emacs-w3m."
223 (let ((text (mm-get-part handle))
225 (charset (mail-content-type-get (mm-handle-type handle) 'charset)))
227 (insert (if charset (mm-decode-string text charset) text))
229 (narrow-to-region b (point))
231 (goto-char (point-min))
232 (when (setq charset (w3m-detect-meta-charset))
233 (delete-region (point-min) (point-max))
234 (insert (mm-decode-string text charset))))
235 (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
237 (w3m-region (point-min) (point-max) nil charset))
238 (when (and mm-inline-text-html-with-w3m-keymap
239 (boundp 'w3m-minor-mode-map)
242 (point-min) (point-max)
243 (list 'keymap w3m-minor-mode-map
244 ;; Put the mark meaning this part was rendered by emacs-w3m.
245 'mm-inline-text-html-with-w3m t))))
246 (mm-handle-set-undisplayer
249 (let (buffer-read-only)
250 (if (functionp 'remove-specifier)
251 (mapcar (lambda (prop)
253 (face-property 'default prop)
255 '(background background-pixmap foreground)))
256 (delete-region ,(point-min-marker)
257 ,(point-max-marker))))))))
259 (defun mm-links-remove-leading-blank ()
260 ;; Delete the annoying three spaces preceding each line of links
262 (goto-char (point-min))
263 (while (re-search-forward "^ " nil t)
264 (delete-region (match-beginning 0) (match-end 0))))
266 (defun mm-inline-wash-with-file (post-func cmd &rest args)
267 (let ((file (mm-make-temp-file
268 (expand-file-name "mm" mm-tmp-directory))))
269 (let ((coding-system-for-write 'binary))
270 (write-region (point-min) (point-max) file nil 'silent))
271 (delete-region (point-min) (point-max))
273 (apply 'call-process cmd nil t nil (mapcar 'eval args))
275 (and post-func (funcall post-func))))
277 (defun mm-inline-wash-with-stdin (post-func cmd &rest args)
278 (let ((coding-system-for-write 'binary))
279 (apply 'call-process-region (point-min) (point-max)
281 (and post-func (funcall post-func)))
283 (defun mm-inline-render-with-file (handle post-func cmd &rest args)
284 (let ((source (mm-get-part handle)))
287 (mm-with-unibyte-buffer
289 (apply 'mm-inline-wash-with-file post-func cmd args)
292 (defun mm-inline-render-with-stdin (handle post-func cmd &rest args)
293 (let ((source (mm-get-part handle)))
296 (mm-with-unibyte-buffer
298 (apply 'mm-inline-wash-with-stdin post-func cmd args)
301 (defun mm-inline-render-with-function (handle func &rest args)
302 (let ((source (mm-get-part handle)))
305 (mm-with-unibyte-buffer
310 (defun mm-inline-text-html (handle)
311 (let* ((func (or mm-inline-text-html-renderer mm-text-html-renderer))
312 (entry (assq func mm-text-html-renderer-alist))
315 (setq func (cdr entry)))
318 (funcall func handle))
320 (apply (car func) handle (cdr func))))))
322 (defun mm-inline-text-vcard (handle)
323 (let (buffer-read-only)
328 (if (fboundp 'vcard-pretty-print)
329 (vcard-pretty-print (mm-get-part handle))
331 (vcard-parse-string (mm-get-part handle)
332 'vcard-standard-filter))))))))
334 (defun mm-inline-text (handle)
336 (type (mm-handle-media-subtype handle))
337 (charset (mail-content-type-get
338 (mm-handle-type handle) 'charset))
340 (if (or (eq charset 'gnus-decoded)
341 ;; This is probably not entirely correct, but
342 ;; makes rfc822 parts with embedded multiparts work.
343 (eq mail-parse-charset 'gnus-decoded))
345 (narrow-to-region (point) (point))