(shr-make-table): Tweak table generation.
[gnus] / lisp / shr.el
index 5b5ac5f..bacb9d9 100644 (file)
@@ -101,7 +101,7 @@ fit these criteria."
          (insert "\n"))
       (if (save-excursion
            (beginning-of-line)
-           (looking-at " *"))
+           (looking-at " *$"))
          (insert "\n")
        (insert "\n\n")))))
 
@@ -114,8 +114,8 @@ fit these criteria."
 (defun shr-tag-u (cont)
   (shr-fontize-cont cont 'underline))
 
-(defun shr-s (cont)
-  (shr-fontize-cont cont 'strikethru))
+(defun shr-tag-s (cont)
+  (shr-fontize-cont cont 'strike-through))
 
 (defun shr-fontize-cont (cont &rest types)
   (let (shr-start)
@@ -142,7 +142,7 @@ fit these criteria."
   (browse-url (widget-get widget :url)))
 
 (defun shr-tag-img (cont)
-  (when (and (plusp (current-column))
+  (when (and (> (current-column) 0)
             (not (eq shr-state 'image)))
     (insert "\n"))
   (let ((start (point-marker)))
@@ -214,14 +214,15 @@ fit these criteria."
       image)))
 
 (defun shr-tag-pre (cont)
-  (let ((shr-folding-mode nil))
+  (let ((shr-folding-mode 'none))
     (shr-ensure-newline)
     (shr-generic cont)
     (shr-ensure-newline)))
 
 (defun shr-tag-blockquote (cont)
+  (shr-ensure-paragraph)
   (let ((shr-indentation (+ shr-indentation 4)))
-    (shr-tag-pre cont)))
+    (shr-generic cont)))
 
 (defun shr-ensure-newline ()
   (unless (zerop (current-column))
@@ -233,34 +234,41 @@ fit these criteria."
     (setq shr-state nil))
   (cond
    ((eq shr-folding-mode 'none)
-    (insert t))
+    (insert text))
    (t
     (let ((first t)
          column)
-      (when (and (string-match "^[ \n]" text)
+      (when (and (string-match "\\`[ \t\n]" text)
                 (not (bolp)))
        (insert " "))
       (dolist (elem (split-string text))
        (setq column (current-column))
-       (when (plusp column)
+       (when (> column 0)
          (cond
-          ((> (+ column (length elem) 1) shr-width)
+          ((and (or (not first)
+                    (eq shr-state 'space))
+                (> (+ column (length elem) 1) shr-width))
            (insert "\n"))
           ((not first)
            (insert " "))))
        (setq first nil)
        (when (and (bolp)
-                  (plusp shr-indentation))
-         (insert (make-string shr-indentation ? )))
+                  (> shr-indentation 0))
+         (shr-indent))
        ;; The shr-start is a special variable that is used to pass
        ;; upwards the first point in the buffer where the text really
        ;; starts.
        (unless shr-start
          (setq shr-start (point)))
        (insert elem))
-      (when (and (string-match "[ \n]$" text)
+      (setq shr-state nil)
+      (when (and (string-match "[ \t\n]\\'" text)
                 (not (bolp)))
-       (insert " "))))))
+       (insert " ")
+       (setq shr-state 'space))))))
+
+(defun shr-indent ()
+  (insert (make-string shr-indentation ? )))
 
 (defun shr-get-image-data (url)
   "Get image data for URL.
@@ -285,15 +293,19 @@ Return a string with image data."
 
 (defun shr-tag-li (cont)
   (shr-ensure-newline)
-  (if (numberp shr-list-mode)
-      (progn
-       (insert (format "%d " shr-list-mode))
-       (setq shr-list-mode (1+ shr-list-mode)))
-    (insert "* "))
-  (shr-generic cont))
+  (let* ((bullet
+         (if (numberp shr-list-mode)
+             (prog1
+                 (format "%d " shr-list-mode)
+               (setq shr-list-mode (1+ shr-list-mode)))
+           "* "))
+        (shr-indentation (+ shr-indentation (length bullet))))
+    (insert bullet)
+    (shr-generic cont)))
 
 (defun shr-tag-br (cont)
-  (shr-ensure-newline)
+  (unless (bobp)
+    (insert "\n"))
   (shr-generic cont))
 
 (defun shr-tag-h1 (cont)
@@ -319,6 +331,138 @@ Return a string with image data."
   (apply #'shr-fontize-cont cont types)
   (shr-ensure-paragraph))
 
+(defun shr-tag-table (cont)
+  (shr-ensure-paragraph)
+  (setq cont (or (cdr (assq 'tbody cont))
+                cont))
+  (let* ((columns (shr-column-specs cont))
+        (suggested-widths (shr-pro-rate-columns columns))
+        (sketch (shr-make-table cont suggested-widths))
+        (sketch-widths (shr-table-widths sketch (length suggested-widths))))
+    (shr-insert-table (shr-make-table cont sketch-widths t) sketch-widths)))
+
+(defun shr-insert-table (table widths)
+  (shr-insert-table-ruler widths)
+  (dolist (row table)
+    (let ((start (point))
+         (height (let ((max 0))
+                   (dolist (column row)
+                     (setq max (max max (cadr column))))
+                   max)))
+      (dotimes (i height)
+       (shr-indent)
+       (insert "|\n"))
+      (dolist (column row)
+       (goto-char start)
+       (let ((lines (split-string (nth 2 column) "\n")))
+         (dolist (line lines)
+           (when (> (length line) 0)
+             (end-of-line)
+             (insert line "|")
+             (forward-line 1)))
+         ;; Add blank lines at padding at the bottom of the TD,
+         ;; possibly.
+         (dotimes (i (- height (length lines)))
+           (end-of-line)
+           (insert (make-string (length (car lines)) ? ) "|")
+           (forward-line 1)))))
+    (shr-insert-table-ruler widths)))
+
+(defun shr-insert-table-ruler (widths)
+  (shr-indent)
+  (insert "+")
+  (dotimes (i (length widths))
+    (insert (make-string (aref widths i) ?-) ?+))
+  (insert "\n"))
+
+(defun shr-table-widths (table length)
+  (let ((widths (make-vector length 0)))
+    (dolist (row table)
+      (let ((i 0))
+       (dolist (column row)
+         (aset widths i (max (aref widths i)
+                             (car column)))
+         (incf i))))
+    widths))
+
+(defun shr-make-table (cons widths &optional fill)
+  (let ((trs nil))
+    (dolist (row cont)
+      (when (eq (car row) 'tr)
+       (let ((i 0)
+             (tds nil))
+         (dolist (column (cdr row))
+           (when (memq (car column) '(td th))
+             (push (shr-render-td (cdr column) (aref widths i) fill)
+                   tds)
+             (setq i (1+ i))))
+         (push (nreverse tds) trs))))
+    (nreverse trs)))
+
+(defun shr-render-td (cont width fill)
+  (with-temp-buffer
+    (let ((shr-width width))
+      (shr-generic cont))
+    (while (re-search-backward "\n *$" nil t)
+      (delete-region (match-beginning 0) (match-end 0)))
+    (goto-char (point-min))
+    (let ((max 0))
+      (while (not (eobp))
+       (end-of-line)
+       (setq max (max max (current-column)))
+       (forward-line 1))
+      (when fill
+       (goto-char (point-min))
+       (while (not (eobp))
+         (end-of-line)
+         (insert (make-string (- width (current-column)) ? ))
+         (forward-line 1)))
+      (list max (count-lines (point-min) (point-max)) (buffer-string)))))
+
+(defun shr-pro-rate-columns (columns)
+  (let ((total-percentage 0)
+       (widths (make-vector (length columns) 0)))
+    (dotimes (i (length columns))
+      (incf total-percentage (aref columns i)))
+    (setq total-percentage (/ 1.0 total-percentage))
+    (dotimes (i (length columns))
+      (aset widths i (max (truncate (* (aref columns i)
+                                      total-percentage
+                                      shr-width))
+                         10)))
+    widths))
+
+;; Return a summary of the number and shape of the TDs in the table.
+(defun shr-column-specs (cont)
+  (let ((columns (make-vector (shr-max-columns cont) 1)))
+    (dolist (row cont)
+      (when (eq (car row) 'tr)
+       (let ((i 0))
+         (dolist (column (cdr row))
+           (when (memq (car column) '(td th))
+             (let ((width (cdr (assq :width (cdr column)))))
+               (when (and width
+                          (string-match "\\([0-9]+\\)%" width))
+                 (aset columns i
+                       (/ (string-to-number (match-string 1 width))
+                          100.0)))))
+           (setq i (1+ i))))))
+    columns))
+
+(defun shr-count (cont elem)
+  (let ((i 0))
+    (dolist (sub cont)
+      (when (eq (car sub) elem)
+       (setq i (1+ i))))
+    i))
+
+(defun shr-max-columns (cont)
+  (let ((max 0))
+    (dolist (row cont)
+      (when (eq (car row) 'tr)
+       (setq max (max max (shr-count (cdr row) 'td)))))
+    max))
+
 (provide 'shr)
 
 ;;; shr.el ends here