;;; shr.el --- Simple HTML Renderer
-;; Copyright (C) 2010-2012 Free Software Foundation, Inc.
+;; Copyright (C) 2010-2013 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Keywords: html
"Images that have URLs matching this regexp will be blocked."
:version "24.1"
:group 'shr
- :type 'regexp)
+ :type '(choice (const nil) regexp))
(defcustom shr-table-horizontal-line ?\s
"Character used to draw horizontal table lines."
;; Public functions and commands.
-(defun shr-visit-file (file)
- "Parse FILE as an HTML document, and render it in a new buffer."
- (interactive "fHTML file name: ")
+(defun shr-render-buffer (buffer)
+ "Display the HTML rendering of the current buffer."
+ (interactive (list (current-buffer)))
(pop-to-buffer "*html*")
(erase-buffer)
(shr-insert-document
- (with-temp-buffer
- (insert-file-contents file)
+ (with-current-buffer buffer
(libxml-parse-html-region (point-min) (point-max))))
(goto-char (point-min)))
+(defun shr-visit-file (file)
+ "Parse FILE as an HTML document, and render it in a new buffer."
+ (interactive "fHTML file name: ")
+ (with-temp-buffer
+ (insert-file-contents file)
+ (shr-render-buffer (current-buffer))))
+
;;;###autoload
(defun shr-insert-document (dom)
"Render the parsed document DOM into the current buffer.
((eq shr-folding-mode 'none)
(insert text))
(t
- (when (and (string-match "\\`[ \t\n ]" text)
+ (when (and (string-match "\\`[ \t\n ]" text)
(not (bolp))
(not (eq (char-after (1- (point))) ? )))
(insert " "))
- (dolist (elem (split-string text "[ \f\t\n\r\v ]+" t))
+ (dolist (elem (split-string text "[ \f\t\n\r\v ]+" t))
(when (and (bolp)
(> shr-indentation 0))
(shr-indent))
(shr-indent))
(end-of-line))
(insert " ")))
- (unless (string-match "[ \t\n ]\\'" text)
+ (unless (string-match "[ \t\r\n ]\\'" text)
(delete-char -1)))))
(defun shr-find-fill-point ()
(shr-char-kinsoku-eol-p (following-char)))))
(goto-char bp)))
((shr-char-kinsoku-eol-p (preceding-char))
- (if (shr-char-kinsoku-eol-p (following-char))
- ;; There are consecutive kinsoku-eol characters.
- (setq failed t)
- (let ((count 4))
- (while
- (progn
- (backward-char 1)
- (and (> (setq count (1- count)) 0)
- (not (memq (preceding-char) (list ?\C-@ ?\n ? )))
- (or (shr-char-kinsoku-eol-p (preceding-char))
- (shr-char-kinsoku-bol-p (following-char)))))))
- (if (setq failed (= (current-column) shr-indentation))
- ;; There's no breakable point that doesn't violate kinsoku,
- ;; so we go to the second best position.
- (if (looking-at "\\(\\c<+\\)\\c<")
- (goto-char (match-end 1))
- (forward-char 1)))))
- (t
- (if (shr-char-kinsoku-bol-p (preceding-char))
- ;; There are consecutive kinsoku-bol characters.
- (setq failed t)
- (let ((count 4))
- (while (and (>= (setq count (1- count)) 0)
+ ;; Find backward the point where kinsoku-eol characters begin.
+ (let ((count 4))
+ (while
+ (progn
+ (backward-char 1)
+ (and (> (setq count (1- count)) 0)
+ (not (memq (preceding-char) (list ?\C-@ ?\n ? )))
+ (or (shr-char-kinsoku-eol-p (preceding-char))
+ (shr-char-kinsoku-bol-p (following-char)))))))
+ (if (setq failed (= (current-column) shr-indentation))
+ ;; There's no breakable point that doesn't violate kinsoku,
+ ;; so we go to the second best position.
+ (if (looking-at "\\(\\c<+\\)\\c<")
+ (goto-char (match-end 1))
+ (forward-char 1))))
+ ((shr-char-kinsoku-bol-p (following-char))
+ ;; Find forward the point where kinsoku-bol characters end.
+ (let ((count 4))
+ (while (progn
+ (forward-char 1)
+ (and (>= (setq count (1- count)) 0)
(shr-char-kinsoku-bol-p (following-char))
- (shr-char-breakable-p (following-char)))
- (forward-char 1))))))
+ (shr-char-breakable-p (following-char))))))))
(when (eq (following-char) ? )
(forward-char 1))))
(not failed)))
(string-match "\\`[a-z]*:" url)
(not shr-base))
url)
+ ((and (string-match "\\`//" url)
+ (string-match "\\`[a-z]*:" shr-base))
+ (concat (match-string 0 shr-base) url))
((and (not (string-match "/\\'" shr-base))
(not (string-match "\\`/" url)))
(concat shr-base "/" url))
(dolist (type types)
(shr-add-font (or shr-start (point)) (point) type))))
+(defun shr-make-overlay (beg end &optional buffer front-advance rear-advance)
+ (let ((overlay (make-overlay beg end buffer front-advance rear-advance)))
+ (overlay-put overlay 'evaporate t)
+ overlay))
+
;; Add an overlay in the region, but avoid putting the font properties
;; on blank text at the start of the line, and the newline at the end,
;; to avoid ugliness.
(while (< (point) end)
(when (bolp)
(skip-chars-forward " "))
- (let ((overlay (make-overlay (point) (min (line-end-position) end))))
+ (let ((overlay (shr-make-overlay (point) (min (line-end-position) end))))
(overlay-put overlay 'face type))
(if (< (line-end-position) end)
(forward-line 1)
(put-text-property start (point) type value))))))))))
(kill-buffer image-buffer)))
+(defun shr-image-from-data (data)
+ "Return an image from the data: URI content DATA."
+ (when (string-match
+ "\\(\\([^/;,]+\\(/[^;,]+\\)?\\)\\(;[^;,]+\\)*\\)?,\\(.*\\)"
+ data)
+ (let ((param (match-string 4 data))
+ (payload (url-unhex-string (match-string 5 data))))
+ (when (string-match "^.*\\(;[ \t]*base64\\)$" param)
+ (setq payload (base64-decode-string payload)))
+ payload)))
+
(defun shr-put-image (data alt &optional flags)
"Put image DATA with a string ALT. Return image."
(if (display-graphic-p)
(overlay-put overlay 'face 'default)))
(insert-image image (or alt "*")))
(put-text-property start (point) 'image-size size)
- (when (image-animated-p image)
+ (when (if (fboundp 'image-multi-frame-p)
+ ;; Only animate multi-frame things that specify a
+ ;; delay; eg animated gifs as opposed to
+ ;; multi-page tiffs. FIXME?
+ (cdr (image-multi-frame-p image))
+ (image-animated-p image))
(image-animate image nil 60)))
image)
(insert alt)))
(when (and (< (setq column (current-column)) width)
(< (setq column (shr-previous-newline-padding-width column))
width))
- (let ((overlay (make-overlay (point) (1+ (point)))))
+ (let ((overlay (shr-make-overlay (point) (1+ (point)))))
(overlay-put overlay 'before-string
(concat
(mapconcat
(shr-fontize-cont cont 'italic))
(defun shr-tag-em (cont)
- (shr-fontize-cont cont 'bold))
+ (shr-fontize-cont cont 'italic))
(defun shr-tag-strong (cont)
(shr-fontize-cont cont 'bold))
(start (point))
shr-start)
(shr-generic cont)
- (shr-urlify (or shr-start start) (shr-expand-url url) title)))
+ (when url
+ (shr-urlify (or shr-start start) (shr-expand-url url) title))))
(defun shr-tag-object (cont)
(let ((start (point))
(member (cdr (assq :width cont)) '("0" "1")))
;; Ignore zero-sized or single-pixel images.
)
+ ((and (not shr-inhibit-images)
+ (string-match "\\`data:" url))
+ (let ((image (shr-image-from-data (substring url (match-end 0)))))
+ (if image
+ (funcall shr-put-image-function image alt)
+ (insert alt))))
((and (not shr-inhibit-images)
(string-match "\\`cid:" url))
(let ((url (substring url (match-end 0)))
(end-of-line)
(insert line shr-table-vertical-line)
(dolist (overlay overlay-line)
- (let ((o (make-overlay (- (point) (nth 0 overlay) 1)
- (- (point) (nth 1 overlay) 1)))
+ (let ((o (shr-make-overlay (- (point) (nth 0 overlay) 1)
+ (- (point) (nth 1 overlay) 1)))
(properties (nth 2 overlay)))
(while properties
(overlay-put o (pop properties) (pop properties)))))
(let ((end (length (car cache))))
(dolist (overlay (cadr cache))
(let ((new-overlay
- (make-overlay (1+ (- end (nth 0 overlay)))
- (1+ (- end (nth 1 overlay)))))
+ (shr-make-overlay (1+ (- end (nth 0 overlay)))
+ (1+ (- end (nth 1 overlay)))))
(properties (nth 2 overlay)))
(while properties
(overlay-put new-overlay
(provide 'shr)
+;; Local Variables:
+;; coding: utf-8
+;; End:
+
;;; shr.el ends here