aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Magne Ingebrigtsen2015-02-10 16:29:05 +1100
committerLars Magne Ingebrigtsen2015-02-10 16:29:05 +1100
commit656caef3505e11b073d59b9c18d3fd21e199d77c (patch)
tree81428a27129c5095c97f73a3741242d20af48b99
parent880415a6a6ec90bd9bf8467a43a41973f78260b3 (diff)
downloademacs-656caef3505e11b073d59b9c18d3fd21e199d77c.tar.gz
emacs-656caef3505e11b073d59b9c18d3fd21e199d77c.zip
Allow using variable-width fonts in eww
* lisp/gnus/mm-decode.el (mm-shr): Only pass the fill column when not using fonts, because limiting the width to what's appropriate for followups doesn't really help when not using proportional fonts. * lisp/net/shr.el (shr-use-fonts): New variable. (shr-fill-text): Rename from "fold". (shr-pixel-column, shr-pixel-region, shr-string-pixel-width): New functions. (shr-insert): Just insert, don't fill the text. Filling is now done afterwards per display unit. (shr-fill-lines, shr-fill-line): New functions to fill text on a per-unit base. (shr-find-fill-point): Take a "beginning" parameter. (shr-indent): Indent using the :width display parameter when using fonts. (shr-parse-style): Ignore "inherit" values, since we already do that. (shr-tag-img): Remove the insertion states. (shr-tag-blockquote): New-style filling. (shr-tag-dd): Ditto. (shr-tag-li): Ditto. (shr-mark-fill): New function to mark lines that need filling. (shr-tag-h1): Use a larger font. (shr-tag-table-1): Get the natural and suggested widths in one rendering. (shr-tag-table): Create the "fixed" version of the table only once so that we can cache data in the table. (shr-insert-table): Get colspan calculations right by having zero-width columns after colspan ones. (shr-expand-alignments): New function to make :align-to specs work right when rendered in one buffer and displayed in another one. (shr-insert-table-ruler): Use :align-to to get the widths right. (shr-make-table): Cache more. (shr-make-table-1): Use the new <td> data layout. (shr-pixel-buffer-width): New function. (shr-render-td): Add a caching layer. (shr-dom-max-natural-width): New function.
-rw-r--r--lisp/ChangeLog35
-rw-r--r--lisp/gnus/ChangeLog12
-rw-r--r--lisp/gnus/mm-decode.el9
-rw-r--r--lisp/net/eww.el20
-rw-r--r--lisp/net/shr.el701
5 files changed, 513 insertions, 264 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index f7dcb840bd6..d8cb245cbeb 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,38 @@
12015-02-10 Lars Ingebrigtsen <larsi@gnus.org>
2
3 * net/shr.el (shr-use-fonts): New variable.
4 (shr-fill-text): Rename from "fold".
5 (shr-pixel-column, shr-pixel-region, shr-string-pixel-width): New
6 functions.
7 (shr-insert): Just insert, don't fill the text. Filling is now
8 done afterwards per display unit.
9 (shr-fill-lines, shr-fill-line): New functions to fill text on a
10 per-unit base.
11 (shr-find-fill-point): Take a "beginning" parameter.
12 (shr-indent): Indent using the :width display parameter when using
13 fonts.
14 (shr-parse-style): Ignore "inherit" values, since we already do that.
15 (shr-tag-img): Remove the insertion states.
16 (shr-tag-blockquote): New-style filling.
17 (shr-tag-dd): Ditto.
18 (shr-tag-li): Ditto.
19 (shr-mark-fill): New function to mark lines that need filling.
20 (shr-tag-h1): Use a larger font.
21 (shr-tag-table-1): Get the natural and suggested widths in one
22 rendering.
23 (shr-tag-table): Create the "fixed" version of the table only once
24 so that we can cache data in the table.
25 (shr-insert-table): Get colspan calculations right by having
26 zero-width columns after colspan ones.
27 (shr-expand-alignments): New function to make :align-to specs work
28 right when rendered in one buffer and displayed in another one.
29 (shr-insert-table-ruler): Use :align-to to get the widths right.
30 (shr-make-table): Cache more.
31 (shr-make-table-1): Use the new <td> data layout.
32 (shr-pixel-buffer-width): New function.
33 (shr-render-td): Add a caching layer.
34 (shr-dom-max-natural-width): New function.
35
12015-02-10 Fabián Ezequiel Gallina <fgallina@gnu.org> 362015-02-10 Fabián Ezequiel Gallina <fgallina@gnu.org>
2 37
3 python.el: Improved shell font lock respecting markers. (Bug#19650) 38 python.el: Improved shell font lock respecting markers. (Bug#19650)
diff --git a/lisp/gnus/ChangeLog b/lisp/gnus/ChangeLog
index 32d3f08f586..ac7e2acab93 100644
--- a/lisp/gnus/ChangeLog
+++ b/lisp/gnus/ChangeLog
@@ -1,3 +1,15 @@
12015-02-10 Lars Ingebrigtsen <larsi@gnus.org>
2
3 * mm-decode.el (mm-shr): Only pass the fill column when not using
4 fonts, because limiting the width to what's appropriate for followups
5 doesn't really help when not using proportional fonts.
6
72015-02-09 Lars Ingebrigtsen <larsi@gnus.org>
8
9 * mm-decode.el (mm-convert-shr-links): Don't overwrite the faces from
10 shr, beacause that breaks folding.
11 (mm-shr): Don't shorten the width when using fonts.
12
12015-02-05 Teodor Zlatanov <tzz@lifelogs.com> 132015-02-05 Teodor Zlatanov <tzz@lifelogs.com>
2 14
3 * gnus-start.el (gnus-save-newsrc-file-check-timestamp): Remove 15 * gnus-start.el (gnus-save-newsrc-file-check-timestamp): Remove
diff --git a/lisp/gnus/mm-decode.el b/lisp/gnus/mm-decode.el
index 311ea7cffff..6c783bbef03 100644
--- a/lisp/gnus/mm-decode.el
+++ b/lisp/gnus/mm-decode.el
@@ -1815,6 +1815,7 @@ If RECURSIVE, search recursively."
1815 (start end &optional base-url)) 1815 (start end &optional base-url))
1816(declare-function shr-insert-document "shr" (dom)) 1816(declare-function shr-insert-document "shr" (dom))
1817(defvar shr-blocked-images) 1817(defvar shr-blocked-images)
1818(defvar shr-use-fonts)
1818(defvar gnus-inhibit-images) 1819(defvar gnus-inhibit-images)
1819(autoload 'gnus-blocked-images "gnus-art") 1820(autoload 'gnus-blocked-images "gnus-art")
1820 1821
@@ -1822,7 +1823,10 @@ If RECURSIVE, search recursively."
1822 ;; Require since we bind its variables. 1823 ;; Require since we bind its variables.
1823 (require 'shr) 1824 (require 'shr)
1824 (let ((article-buffer (current-buffer)) 1825 (let ((article-buffer (current-buffer))
1825 (shr-width fill-column) 1826 (shr-width (if (and (boundp 'shr-use-fonts)
1827 shr-use-fonts)
1828 nil
1829 fill-column))
1826 (shr-content-function (lambda (id) 1830 (shr-content-function (lambda (id)
1827 (let ((handle (mm-get-content-id id))) 1831 (let ((handle (mm-get-content-id id)))
1828 (when handle 1832 (when handle
@@ -1890,12 +1894,15 @@ If RECURSIVE, search recursively."
1890 (< start (point-max))) 1894 (< start (point-max)))
1891 (when (setq start (text-property-not-all start (point-max) 'shr-url nil)) 1895 (when (setq start (text-property-not-all start (point-max) 'shr-url nil))
1892 (setq end (next-single-property-change start 'shr-url nil (point-max))) 1896 (setq end (next-single-property-change start 'shr-url nil (point-max)))
1897 (setq face (get-text-property start 'face))
1893 (widget-convert-button 1898 (widget-convert-button
1894 'url-link start end 1899 'url-link start end
1895 :help-echo (get-text-property start 'help-echo) 1900 :help-echo (get-text-property start 'help-echo)
1896 :keymap shr-map 1901 :keymap shr-map
1897 (get-text-property start 'shr-url)) 1902 (get-text-property start 'shr-url))
1898 (put-text-property start end 'local-map nil) 1903 (put-text-property start end 'local-map nil)
1904 (dolist (overlay (overlays-at start))
1905 (overlay-put overlay 'face nil))
1899 (setq start end))))) 1906 (setq start end)))))
1900 1907
1901(defun mm-handle-filename (handle) 1908(defun mm-handle-filename (handle)
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index ec7a0baacf6..c401701f255 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -409,7 +409,6 @@ See the `eww-search-prefix' variable for the search engine used."
409 (form . eww-tag-form) 409 (form . eww-tag-form)
410 (input . eww-tag-input) 410 (input . eww-tag-input)
411 (textarea . eww-tag-textarea) 411 (textarea . eww-tag-textarea)
412 (body . eww-tag-body)
413 (select . eww-tag-select) 412 (select . eww-tag-select)
414 (link . eww-tag-link) 413 (link . eww-tag-link)
415 (a . eww-tag-a)))) 414 (a . eww-tag-a))))
@@ -495,15 +494,6 @@ See the `eww-search-prefix' variable for the search engine used."
495 (replace-regexp-in-string "[ \t\r\n]+" " " (dom-text dom)))) 494 (replace-regexp-in-string "[ \t\r\n]+" " " (dom-text dom))))
496 (eww-update-header-line-format)) 495 (eww-update-header-line-format))
497 496
498(defun eww-tag-body (dom)
499 (let* ((start (point))
500 (fgcolor (or (dom-attr dom 'fgcolor) (dom-attr dom 'text)))
501 (bgcolor (dom-attr dom 'bgcolor))
502 (shr-stylesheet (list (cons 'color fgcolor)
503 (cons 'background-color bgcolor))))
504 (shr-generic dom)
505 (shr-colorize-region start (point) fgcolor bgcolor)))
506
507(defun eww-display-raw (buffer &optional encode) 497(defun eww-display-raw (buffer &optional encode)
508 (let ((data (buffer-substring (point) (point-max)))) 498 (let ((data (buffer-substring (point) (point-max))))
509 (unless (buffer-live-p buffer) 499 (unless (buffer-live-p buffer)
@@ -653,6 +643,7 @@ the like."
653 (define-key map "H" 'eww-list-histories) 643 (define-key map "H" 'eww-list-histories)
654 (define-key map "E" 'eww-set-character-encoding) 644 (define-key map "E" 'eww-set-character-encoding)
655 (define-key map "S" 'eww-list-buffers) 645 (define-key map "S" 'eww-list-buffers)
646 (define-key map "F" 'eww-toggle-fonts)
656 647
657 (define-key map "b" 'eww-add-bookmark) 648 (define-key map "b" 'eww-add-bookmark)
658 (define-key map "B" 'eww-list-bookmarks) 649 (define-key map "B" 'eww-list-bookmarks)
@@ -1425,6 +1416,15 @@ Differences in #targets are ignored."
1425 (eww-reload nil 'utf-8) 1416 (eww-reload nil 'utf-8)
1426 (eww-reload nil charset))) 1417 (eww-reload nil charset)))
1427 1418
1419(defun eww-toggle-fonts ()
1420 "Toggle whether to use monospaced or font-enabled layouts."
1421 (interactive)
1422 (message "Fonts are now %s"
1423 (if (setq shr-use-fonts (not shr-use-fonts))
1424 "on"
1425 "off"))
1426 (eww-reload))
1427
1428;;; Bookmarks code 1428;;; Bookmarks code
1429 1429
1430(defvar eww-bookmarks nil) 1430(defvar eww-bookmarks nil)
diff --git a/lisp/net/shr.el b/lisp/net/shr.el
index 59c277b01c2..06a75a46bf2 100644
--- a/lisp/net/shr.el
+++ b/lisp/net/shr.el
@@ -57,6 +57,12 @@ fit these criteria."
57 :group 'shr 57 :group 'shr
58 :type '(choice (const nil) regexp)) 58 :type '(choice (const nil) regexp))
59 59
60(defcustom shr-use-fonts nil
61 "If non-nil, use proportional fonts for text."
62 :version "25.1"
63 :group 'shr
64 :type 'boolean)
65
60(defcustom shr-table-horizontal-line nil 66(defcustom shr-table-horizontal-line nil
61 "Character used to draw horizontal table lines. 67 "Character used to draw horizontal table lines.
62If nil, don't draw horizontal table lines." 68If nil, don't draw horizontal table lines."
@@ -132,10 +138,9 @@ cid: URL as the argument.")
132;;; Internal variables. 138;;; Internal variables.
133 139
134(defvar shr-folding-mode nil) 140(defvar shr-folding-mode nil)
135(defvar shr-state nil)
136(defvar shr-start nil) 141(defvar shr-start nil)
137(defvar shr-indentation 0) 142(defvar shr-indentation 0)
138(defvar shr-internal-width (or shr-width (1- (window-width)))) 143(defvar shr-internal-width nil)
139(defvar shr-list-mode nil) 144(defvar shr-list-mode nil)
140(defvar shr-content-cache nil) 145(defvar shr-content-cache nil)
141(defvar shr-kinsoku-shorten nil) 146(defvar shr-kinsoku-shorten nil)
@@ -149,6 +154,9 @@ cid: URL as the argument.")
149(defvar shr-target-id nil) 154(defvar shr-target-id nil)
150(defvar shr-inhibit-decoration nil) 155(defvar shr-inhibit-decoration nil)
151(defvar shr-table-separator-length 1) 156(defvar shr-table-separator-length 1)
157(defvar shr-table-separator-pixel-width 0)
158(defvar shr-table-id nil)
159(defvar shr-current-font nil)
152 160
153(defvar shr-map 161(defvar shr-map
154 (let ((map (make-sparse-keymap))) 162 (let ((map (make-sparse-keymap)))
@@ -202,13 +210,22 @@ DOM should be a parse tree as generated by
202`libxml-parse-html-region' or similar." 210`libxml-parse-html-region' or similar."
203 (setq shr-content-cache nil) 211 (setq shr-content-cache nil)
204 (let ((start (point)) 212 (let ((start (point))
205 (shr-state nil)
206 (shr-start nil) 213 (shr-start nil)
207 (shr-base nil) 214 (shr-base nil)
208 (shr-depth 0) 215 (shr-depth 0)
216 (shr-table-id 0)
209 (shr-warning nil) 217 (shr-warning nil)
210 (shr-internal-width (or shr-width (1- (window-width))))) 218 (shr-table-separator-pixel-width (shr-string-pixel-width "-"))
219 (shr-internal-width (or (and shr-width
220 (if (not shr-use-fonts)
221 shr-width
222 (* shr-width (frame-char-width))))
223 (if (not shr-use-fonts)
224 (- (window-width) 2)
225 (- (window-pixel-width)
226 (* (frame-fringe-width) 2))))))
211 (shr-descend dom) 227 (shr-descend dom)
228 (shr-fill-lines start (point))
212 (shr-remove-trailing-whitespace start (point)) 229 (shr-remove-trailing-whitespace start (point))
213 (when shr-warning 230 (when shr-warning
214 (message "%s" shr-warning)))) 231 (message "%s" shr-warning))))
@@ -303,7 +320,7 @@ redirects somewhere else."
303 (let ((text (get-text-property (point) 'shr-alt))) 320 (let ((text (get-text-property (point) 'shr-alt)))
304 (if (not text) 321 (if (not text)
305 (message "No image under point") 322 (message "No image under point")
306 (message "%s" (shr-fold-text text))))) 323 (message "%s" (shr-fill-text text)))))
307 324
308(defun shr-browse-image (&optional copy-url) 325(defun shr-browse-image (&optional copy-url)
309 "Browse the image under point. 326 "Browse the image under point.
@@ -414,14 +431,14 @@ size, and full-buffer size."
414 (cdr (assq 'color shr-stylesheet)) 431 (cdr (assq 'color shr-stylesheet))
415 (cdr (assq 'background-color shr-stylesheet)))))))) 432 (cdr (assq 'background-color shr-stylesheet))))))))
416 433
417(defun shr-fold-text (text) 434(defun shr-fill-text (text)
418 (if (zerop (length text)) 435 (if (zerop (length text))
419 text 436 text
420 (with-temp-buffer 437 (with-temp-buffer
421 (let ((shr-indentation 0) 438 (let ((shr-indentation 0)
422 (shr-state nil)
423 (shr-start nil) 439 (shr-start nil)
424 (shr-internal-width (window-width))) 440 (shr-internal-width (- (window-pixel-width)
441 (* (frame-fringe-width) 2))))
425 (shr-insert text) 442 (shr-insert text)
426 (buffer-string))))) 443 (buffer-string)))))
427 444
@@ -447,76 +464,123 @@ size, and full-buffer size."
447(unless (shr-char-kinsoku-bol-p (make-char 'japanese-jisx0208 33 35)) 464(unless (shr-char-kinsoku-bol-p (make-char 'japanese-jisx0208 33 35))
448 (load "kinsoku" nil t)) 465 (load "kinsoku" nil t))
449 466
467(defun shr-pixel-column ()
468 (if (not shr-use-fonts)
469 (current-column)
470 (if (not (get-buffer-window (current-buffer)))
471 (save-window-excursion
472 (set-window-buffer nil (current-buffer))
473 (car (window-text-pixel-size nil (line-beginning-position) (point))))
474 (car (window-text-pixel-size nil (line-beginning-position) (point))))))
475
476(defun shr-pixel-region ()
477 (- (shr-pixel-column)
478 (save-excursion
479 (goto-char (mark))
480 (shr-pixel-column))))
481
482(defun shr-string-pixel-width (string)
483 (if (not shr-use-fonts)
484 (length string)
485 (with-temp-buffer
486 (insert string)
487 (shr-pixel-column))))
488
450(defun shr-insert (text) 489(defun shr-insert (text)
451 (when (and (eq shr-state 'image) 490 (when (and (not (bolp))
452 (not (bolp)) 491 (get-text-property (1- (point)) 'image-url))
453 (not (string-match "\\`[ \t\n]+\\'" text))) 492 (insert "\n"))
454 (insert "\n")
455 (setq shr-state nil))
456 (cond 493 (cond
457 ((eq shr-folding-mode 'none) 494 ((eq shr-folding-mode 'none)
458 (insert text)) 495 (insert text))
459 (t 496 (t
460 (when (and (string-match "\\`[ \t\n ]" text) 497 (when (and (string-match "\\`[ \t\n\r ]" text)
461 (not (bolp)) 498 (not (bolp))
462 (not (eq (char-after (1- (point))) ? ))) 499 (not (eq (char-after (1- (point))) ? )))
463 (insert " ")) 500 (insert " "))
464 (dolist (elem (split-string text "[ \f\t\n\r\v ]+" t)) 501 (let ((start (point))
465 (when (and (bolp) 502 (bolp (bolp)))
466 (> shr-indentation 0)) 503 (insert text)
467 (shr-indent)) 504 (save-restriction
468 ;; No space is needed behind a wide character categorized as 505 (narrow-to-region start (point))
469 ;; kinsoku-bol, between characters both categorized as nospace, 506 (goto-char start)
470 ;; or at the beginning of a line. 507 (when (looking-at "[ \t\n\r ]+")
471 (let (prev) 508 (replace-match "" t t))
472 (when (and (> (current-column) shr-indentation) 509 (while (re-search-forward "[ \t\n\r ]+" nil t)
473 (eq (preceding-char) ? ) 510 (replace-match " " t t))
474 (or (= (line-beginning-position) (1- (point))) 511 (goto-char (point-max)))
475 (and (shr-char-breakable-p 512 ;; We may have removed everything we inserted if if was just
476 (setq prev (char-after (- (point) 2)))) 513 ;; spaces.
477 (shr-char-kinsoku-bol-p prev)) 514 (unless (= start (point))
478 (and (shr-char-nospace-p prev) 515 ;; Mark all lines that should possibly be folded afterwards.
479 (shr-char-nospace-p (aref elem 0))))) 516 (when bolp
480 (delete-char -1))) 517 (shr-mark-fill start))
481 ;; The shr-start is a special variable that is used to pass 518 (when shr-use-fonts
482 ;; upwards the first point in the buffer where the text really 519 (add-face-text-property start (point)
483 ;; starts. 520 (or shr-current-font 'variable-pitch)
484 (unless shr-start 521 t)))))))
485 (setq shr-start (point))) 522
486 (insert elem) 523(defun shr-fill-lines (start end)
487 (setq shr-state nil) 524 (if (<= shr-internal-width 0)
488 (let (found) 525 nil
489 (while (and (> (current-column) shr-internal-width) 526 (save-restriction
490 (> shr-internal-width 0) 527 (narrow-to-region start end)
491 (progn 528 (goto-char start)
492 (setq found (shr-find-fill-point)) 529 (when (get-text-property (point) 'shr-indentation)
493 (not (eolp)))) 530 (shr-fill-line))
494 (when (eq (preceding-char) ? ) 531 (while (setq start (next-single-property-change start 'shr-indentation))
495 (delete-char -1)) 532 (goto-char start)
496 (insert "\n") 533 (when (bolp)
497 (unless found 534 (shr-fill-line)))
498 ;; No space is needed at the beginning of a line. 535 (goto-char (point-max)))))
499 (when (eq (following-char) ? ) 536
500 (delete-char 1))) 537(defun shr-vertical-motion (column)
501 (when (> shr-indentation 0) 538 (if (not shr-use-fonts)
502 (shr-indent)) 539 (move-to-column column)
503 (end-of-line)) 540 (unless (eolp)
504 (if (<= (current-column) shr-internal-width) 541 (forward-char 1))
505 (insert " ") 542 (vertical-motion (cons (/ column (frame-char-width)) 0))
506 ;; In case we couldn't get a valid break point (because of a 543 (unless (eolp)
507 ;; word that's longer than `shr-internal-width'), just break anyway. 544 (forward-char 1))))
508 (insert "\n") 545
509 (when (> shr-indentation 0) 546(defun shr-fill-line ()
510 (shr-indent))))) 547 (let ((shr-indentation (get-text-property (point) 'shr-indentation))
511 (unless (string-match "[ \t\r\n ]\\'" text) 548 (continuation (get-text-property
512 (delete-char -1))))) 549 (point) 'shr-continuation-indentation))
513 550 start)
514(defun shr-find-fill-point () 551 (put-text-property (point) (1+ (point)) 'shr-indentation nil)
515 (when (> (move-to-column shr-internal-width) shr-internal-width) 552 (shr-indent)
516 (backward-char 1)) 553 (setq start (point))
554 (setq shr-indentation (or continuation shr-indentation))
555 (shr-vertical-motion shr-internal-width)
556 (when (looking-at " $")
557 (delete-region (point) (line-end-position)))
558 (while (not (eolp))
559 ;; We have to do some folding. First find the first
560 ;; previous point suitable for folding.
561 (if (or (not (shr-find-fill-point (line-beginning-position)))
562 (= (point) start))
563 ;; We had unbreakable text (for this width), so just go to
564 ;; the first space and carry on.
565 (progn
566 (beginning-of-line)
567 (skip-chars-forward " ")
568 (search-forward " " (line-end-position) 'move)))
569 ;; Success; continue.
570 (when (= (preceding-char) ?\s)
571 (delete-char -1))
572 (insert "\n")
573 (shr-indent)
574 (setq start (point))
575 (shr-vertical-motion shr-internal-width)
576 (when (looking-at " $")
577 (delete-region (point) (line-end-position))))))
578
579(defun shr-find-fill-point (start)
517 (let ((bp (point)) 580 (let ((bp (point))
581 (end (point))
518 failed) 582 failed)
519 (while (not (or (setq failed (<= (current-column) shr-indentation)) 583 (while (not (or (setq failed (<= (point) start))
520 (eq (preceding-char) ? ) 584 (eq (preceding-char) ? )
521 (eq (following-char) ? ) 585 (eq (following-char) ? )
522 (shr-char-breakable-p (preceding-char)) 586 (shr-char-breakable-p (preceding-char))
@@ -547,12 +611,12 @@ size, and full-buffer size."
547 (while (and (not (memq (preceding-char) (list ?\C-@ ?\n ? ))) 611 (while (and (not (memq (preceding-char) (list ?\C-@ ?\n ? )))
548 (shr-char-kinsoku-eol-p (preceding-char))) 612 (shr-char-kinsoku-eol-p (preceding-char)))
549 (backward-char 1)) 613 (backward-char 1))
550 (when (setq failed (<= (current-column) shr-indentation)) 614 (when (setq failed (<= (point) start))
551 ;; There's no breakable point that doesn't violate kinsoku, 615 ;; There's no breakable point that doesn't violate kinsoku,
552 ;; so we look for the second best position. 616 ;; so we look for the second best position.
553 (while (and (progn 617 (while (and (progn
554 (forward-char 1) 618 (forward-char 1)
555 (<= (current-column) shr-internal-width)) 619 (<= (point) end))
556 (progn 620 (progn
557 (setq bp (point)) 621 (setq bp (point))
558 (shr-char-kinsoku-eol-p (following-char))))) 622 (shr-char-kinsoku-eol-p (following-char)))))
@@ -567,7 +631,7 @@ size, and full-buffer size."
567 (not (memq (preceding-char) (list ?\C-@ ?\n ? ))) 631 (not (memq (preceding-char) (list ?\C-@ ?\n ? )))
568 (or (shr-char-kinsoku-eol-p (preceding-char)) 632 (or (shr-char-kinsoku-eol-p (preceding-char))
569 (shr-char-kinsoku-bol-p (following-char))))))) 633 (shr-char-kinsoku-bol-p (following-char)))))))
570 (when (setq failed (<= (current-column) shr-indentation)) 634 (when (setq failed (<= (point) start))
571 ;; There's no breakable point that doesn't violate kinsoku, 635 ;; There's no breakable point that doesn't violate kinsoku,
572 ;; so we go to the second best position. 636 ;; so we go to the second best position.
573 (if (looking-at "\\(\\c<+\\)\\c<") 637 (if (looking-at "\\(\\c<+\\)\\c<")
@@ -664,13 +728,18 @@ size, and full-buffer size."
664 728
665(defun shr-indent () 729(defun shr-indent ()
666 (when (> shr-indentation 0) 730 (when (> shr-indentation 0)
667 (insert (make-string shr-indentation ? )))) 731 (insert
732 (if (not shr-use-fonts)
733 (make-string shr-indentation ?\s)
734 (propertize " "
735 'display
736 `(space :width (,shr-indentation)))))))
668 737
669(defun shr-fontize-dom (dom &rest types) 738(defun shr-fontize-dom (dom &rest types)
670 (let (shr-start) 739 (let ((start (point)))
671 (shr-generic dom) 740 (shr-generic dom)
672 (dolist (type types) 741 (dolist (type types)
673 (shr-add-font (or shr-start (point)) (point) type)))) 742 (shr-add-font start (point) type))))
674 743
675;; Add face to the region, but avoid putting the font properties on 744;; Add face to the region, but avoid putting the font properties on
676;; blank text at the start of the line, and the newline at the end, to 745;; blank text at the start of the line, and the newline at the end, to
@@ -1070,13 +1139,11 @@ ones, in case fg and bg are nil."
1070 1139
1071(defun shr-tag-p (dom) 1140(defun shr-tag-p (dom)
1072 (shr-ensure-paragraph) 1141 (shr-ensure-paragraph)
1073 (shr-indent)
1074 (shr-generic dom) 1142 (shr-generic dom)
1075 (shr-ensure-paragraph)) 1143 (shr-ensure-paragraph))
1076 1144
1077(defun shr-tag-div (dom) 1145(defun shr-tag-div (dom)
1078 (shr-ensure-newline) 1146 (shr-ensure-newline)
1079 (shr-indent)
1080 (shr-generic dom) 1147 (shr-generic dom)
1081 (shr-ensure-newline)) 1148 (shr-ensure-newline))
1082 1149
@@ -1116,9 +1183,10 @@ ones, in case fg and bg are nil."
1116 (value (replace-regexp-in-string "^ +\\| +$" "" (cadr elem)))) 1183 (value (replace-regexp-in-string "^ +\\| +$" "" (cadr elem))))
1117 (when (string-match " *!important\\'" value) 1184 (when (string-match " *!important\\'" value)
1118 (setq value (substring value 0 (match-beginning 0)))) 1185 (setq value (substring value 0 (match-beginning 0))))
1119 (push (cons (intern name obarray) 1186 (unless (equal value "inherit")
1120 value) 1187 (push (cons (intern name obarray)
1121 plist))))) 1188 value)
1189 plist))))))
1122 plist))) 1190 plist)))
1123 1191
1124(defun shr-tag-base (dom) 1192(defun shr-tag-base (dom)
@@ -1245,8 +1313,7 @@ The preference is a float determined from `shr-prefer-media-type'."
1245 (when (or url 1313 (when (or url
1246 (and dom 1314 (and dom
1247 (> (length (dom-attr dom 'src)) 0))) 1315 (> (length (dom-attr dom 'src)) 0)))
1248 (when (and (> (current-column) 0) 1316 (when (> (current-column) 0)
1249 (not (eq shr-state 'image)))
1250 (insert "\n")) 1317 (insert "\n"))
1251 (let ((alt (dom-attr dom 'alt)) 1318 (let ((alt (dom-attr dom 'alt))
1252 (url (shr-expand-url (or url (dom-attr dom 'src))))) 1319 (url (shr-expand-url (or url (dom-attr dom 'src)))))
@@ -1276,10 +1343,9 @@ The preference is a float determined from `shr-prefer-media-type'."
1276 (and shr-blocked-images 1343 (and shr-blocked-images
1277 (string-match shr-blocked-images url))) 1344 (string-match shr-blocked-images url)))
1278 (setq shr-start (point)) 1345 (setq shr-start (point))
1279 (let ((shr-state 'space)) 1346 (if (> (string-width alt) 8)
1280 (if (> (string-width alt) 8) 1347 (shr-insert (truncate-string-to-width alt 8))
1281 (shr-insert (truncate-string-to-width alt 8)) 1348 (shr-insert alt)))
1282 (shr-insert alt))))
1283 ((and (not shr-ignore-cache) 1349 ((and (not shr-ignore-cache)
1284 (url-is-cached (shr-encode-url url))) 1350 (url-is-cached (shr-encode-url url)))
1285 (funcall shr-put-image-function (shr-get-image-data url) alt)) 1351 (funcall shr-put-image-function (shr-get-image-data url) alt))
@@ -1301,22 +1367,24 @@ The preference is a float determined from `shr-prefer-media-type'."
1301 (put-text-property start (point) 'image-displayer 1367 (put-text-property start (point) 'image-displayer
1302 (shr-image-displayer shr-content-function)) 1368 (shr-image-displayer shr-content-function))
1303 (put-text-property start (point) 'help-echo 1369 (put-text-property start (point) 'help-echo
1304 (shr-fold-text (or (dom-attr dom 'title) alt)))) 1370 (shr-fill-text
1305 (setq shr-state 'image))))) 1371 (or (dom-attr dom 'title) alt))))))))
1306 1372
1307(defun shr-tag-pre (dom) 1373(defun shr-tag-pre (dom)
1308 (let ((shr-folding-mode 'none)) 1374 (let ((shr-folding-mode 'none)
1375 (shr-current-font 'default))
1309 (shr-ensure-newline) 1376 (shr-ensure-newline)
1310 (shr-indent)
1311 (shr-generic dom) 1377 (shr-generic dom)
1312 (shr-ensure-newline))) 1378 (shr-ensure-newline)))
1313 1379
1314(defun shr-tag-blockquote (dom) 1380(defun shr-tag-blockquote (dom)
1315 (shr-ensure-paragraph) 1381 (shr-ensure-paragraph)
1316 (shr-indent) 1382 (let ((start (point))
1317 (let ((shr-indentation (+ shr-indentation 4))) 1383 (shr-indentation (+ shr-indentation
1318 (shr-generic dom)) 1384 (* 4 shr-table-separator-pixel-width))))
1319 (shr-ensure-paragraph)) 1385 (shr-generic dom)
1386 (shr-ensure-paragraph)
1387 (shr-mark-fill start)))
1320 1388
1321(defun shr-tag-dl (dom) 1389(defun shr-tag-dl (dom)
1322 (shr-ensure-paragraph) 1390 (shr-ensure-paragraph)
@@ -1330,7 +1398,8 @@ The preference is a float determined from `shr-prefer-media-type'."
1330 1398
1331(defun shr-tag-dd (dom) 1399(defun shr-tag-dd (dom)
1332 (shr-ensure-newline) 1400 (shr-ensure-newline)
1333 (let ((shr-indentation (+ shr-indentation 4))) 1401 (let ((shr-indentation (+ shr-indentation
1402 (* 4 shr-table-separator-pixel-width))))
1334 (shr-generic dom))) 1403 (shr-generic dom)))
1335 1404
1336(defun shr-tag-ul (dom) 1405(defun shr-tag-ul (dom)
@@ -1347,16 +1416,26 @@ The preference is a float determined from `shr-prefer-media-type'."
1347 1416
1348(defun shr-tag-li (dom) 1417(defun shr-tag-li (dom)
1349 (shr-ensure-newline) 1418 (shr-ensure-newline)
1350 (shr-indent) 1419 (let ((start (point)))
1351 (let* ((bullet 1420 (let* ((bullet
1352 (if (numberp shr-list-mode) 1421 (if (numberp shr-list-mode)
1353 (prog1 1422 (prog1
1354 (format "%d " shr-list-mode) 1423 (format "%d " shr-list-mode)
1355 (setq shr-list-mode (1+ shr-list-mode))) 1424 (setq shr-list-mode (1+ shr-list-mode)))
1356 shr-bullet)) 1425 shr-bullet)))
1357 (shr-indentation (+ shr-indentation (length bullet)))) 1426 (insert bullet)
1358 (insert bullet) 1427 (shr-mark-fill start)
1359 (shr-generic dom))) 1428 (let ((shr-indentation (+ shr-indentation
1429 (shr-string-pixel-width bullet))))
1430 (put-text-property start (1+ start)
1431 'shr-continuation-indentation shr-indentation)
1432 (shr-generic dom)))))
1433
1434(defun shr-mark-fill (start)
1435 ;; We may not have inserted any text to fill.
1436 (unless (= start (point))
1437 (put-text-property start (1+ start)
1438 'shr-indentation shr-indentation)))
1360 1439
1361(defun shr-tag-br (dom) 1440(defun shr-tag-br (dom)
1362 (when (and (not (bobp)) 1441 (when (and (not (bobp))
@@ -1365,15 +1444,14 @@ The preference is a float determined from `shr-prefer-media-type'."
1365 (or (not (bolp)) 1444 (or (not (bolp))
1366 (and (> (- (point) 2) (point-min)) 1445 (and (> (- (point) 2) (point-min))
1367 (not (= (char-after (- (point) 2)) ?\n))))) 1446 (not (= (char-after (- (point) 2)) ?\n)))))
1368 (insert "\n") 1447 (insert "\n"))
1369 (shr-indent))
1370 (shr-generic dom)) 1448 (shr-generic dom))
1371 1449
1372(defun shr-tag-span (dom) 1450(defun shr-tag-span (dom)
1373 (shr-generic dom)) 1451 (shr-generic dom))
1374 1452
1375(defun shr-tag-h1 (dom) 1453(defun shr-tag-h1 (dom)
1376 (shr-heading dom 'bold 'underline)) 1454 (shr-heading dom '(variable-pitch (:height 1.3 :weight bold))))
1377 1455
1378(defun shr-tag-h2 (dom) 1456(defun shr-tag-h2 (dom)
1379 (shr-heading dom 'bold)) 1457 (shr-heading dom 'bold))
@@ -1392,7 +1470,8 @@ The preference is a float determined from `shr-prefer-media-type'."
1392 1470
1393(defun shr-tag-hr (_dom) 1471(defun shr-tag-hr (_dom)
1394 (shr-ensure-newline) 1472 (shr-ensure-newline)
1395 (insert (make-string shr-internal-width shr-hr-line) "\n")) 1473 ;; FIXME: Should try to make a line of the required pixel size.
1474 (insert (make-string (window-width) shr-hr-line) "\n"))
1396 1475
1397(defun shr-tag-title (dom) 1476(defun shr-tag-title (dom)
1398 (shr-heading dom 'bold 'underline)) 1477 (shr-heading dom 'bold 'underline))
@@ -1424,20 +1503,23 @@ The preference is a float determined from `shr-prefer-media-type'."
1424 (shr-kinsoku-shorten t) 1503 (shr-kinsoku-shorten t)
1425 ;; Find all suggested widths. 1504 ;; Find all suggested widths.
1426 (columns (shr-column-specs dom)) 1505 (columns (shr-column-specs dom))
1427 ;; Compute how many characters wide each TD should be. 1506 ;; Compute how many pixels wide each TD should be.
1428 (suggested-widths (shr-pro-rate-columns columns)) 1507 (suggested-widths (shr-pro-rate-columns columns))
1429 ;; Do a "test rendering" to see how big each TD is (this can 1508 ;; Do a "test rendering" to see how big each TD is (this can
1430 ;; be smaller (if there's little text) or bigger (if there's 1509 ;; be smaller (if there's little text) or bigger (if there's
1431 ;; unbreakable text). 1510 ;; unbreakable text).
1432 (sketch (shr-make-table dom suggested-widths)) 1511 (elems (or (dom-attr dom 'shr-suggested-widths)
1433 ;; Compute the "natural" width by setting each column to 500 1512 (shr-make-table dom suggested-widths nil
1434 ;; characters and see how wide they really render. 1513 'shr-suggested-widths)))
1435 (natural (shr-make-table dom (make-vector (length columns) 500))) 1514 (sketch (loop for line in elems
1515 collect (mapcar #'car line)))
1516 (natural (loop for line in elems
1517 collect (mapcar #'cdr line)))
1436 (sketch-widths (shr-table-widths sketch natural suggested-widths))) 1518 (sketch-widths (shr-table-widths sketch natural suggested-widths)))
1437 ;; This probably won't work very well. 1519 ;; This probably won't work very well.
1438 (when (> (+ (loop for width across sketch-widths 1520 (when (> (+ (loop for width across sketch-widths
1439 summing (1+ width)) 1521 summing (1+ width))
1440 shr-indentation 1) 1522 shr-indentation shr-table-separator-pixel-width)
1441 (frame-width)) 1523 (frame-width))
1442 (setq truncate-lines t)) 1524 (setq truncate-lines t))
1443 ;; Then render the table again with these new "hard" widths. 1525 ;; Then render the table again with these new "hard" widths.
@@ -1466,64 +1548,71 @@ The preference is a float determined from `shr-prefer-media-type'."
1466 ;; Try to output it anyway. 1548 ;; Try to output it anyway.
1467 (shr-generic dom) 1549 (shr-generic dom)
1468 ;; It's a real table, so render it. 1550 ;; It's a real table, so render it.
1469 (shr-tag-table-1 1551 (if (dom-attr dom 'shr-fixed-table)
1470 (nconc 1552 (shr-tag-table-1 dom)
1471 (list 'table nil) 1553 ;; Only fix up the table once.
1472 (if caption `((tr nil (td nil ,@caption)))) 1554 (let ((table
1473 (cond (header 1555 (nconc
1474 (if footer 1556 (list 'table nil)
1475 ;; header + body + footer 1557 (if caption `((tr nil (td nil ,@caption))))
1476 (if (= nheader nbody) 1558 (cond
1477 (if (= nbody nfooter) 1559 (header
1478 `((tr nil (td nil (table nil 1560 (if footer
1479 (tbody nil ,@header 1561 ;; header + body + footer
1480 ,@body ,@footer))))) 1562 (if (= nheader nbody)
1481 (nconc `((tr nil (td nil (table nil 1563 (if (= nbody nfooter)
1482 (tbody nil ,@header 1564 `((tr nil (td nil (table nil
1483 ,@body))))) 1565 (tbody nil ,@header
1484 (if (= nfooter 1) 1566 ,@body ,@footer)))))
1485 footer 1567 (nconc `((tr nil (td nil (table nil
1486 `((tr nil (td nil (table 1568 (tbody nil ,@header
1487 nil (tbody
1488 nil ,@footer))))))))
1489 (nconc `((tr nil (td nil (table nil (tbody
1490 nil ,@header)))))
1491 (if (= nbody nfooter)
1492 `((tr nil (td nil (table
1493 nil (tbody nil ,@body
1494 ,@footer)))))
1495 (nconc `((tr nil (td nil (table
1496 nil (tbody nil
1497 ,@body))))) 1569 ,@body)))))
1498 (if (= nfooter 1) 1570 (if (= nfooter 1)
1499 footer 1571 footer
1500 `((tr nil (td nil (table 1572 `((tr nil (td nil (table
1501 nil 1573 nil (tbody
1502 (tbody 1574 nil ,@footer))))))))
1503 nil 1575 (nconc `((tr nil (td nil (table nil (tbody
1504 ,@footer)))))))))) 1576 nil ,@header)))))
1505 ;; header + body 1577 (if (= nbody nfooter)
1506 (if (= nheader nbody) 1578 `((tr nil (td nil (table
1507 `((tr nil (td nil (table nil (tbody nil ,@header 1579 nil (tbody nil ,@body
1508 ,@body))))) 1580 ,@footer)))))
1509 (if (= nheader 1) 1581 (nconc `((tr nil (td nil (table
1510 `(,@header (tr nil (td nil (table 1582 nil (tbody nil
1511 nil (tbody nil ,@body))))) 1583 ,@body)))))
1512 `((tr nil (td nil (table nil (tbody nil ,@header)))) 1584 (if (= nfooter 1)
1513 (tr nil (td nil (table nil (tbody nil ,@body))))))))) 1585 footer
1514 (footer 1586 `((tr nil (td nil (table
1515 ;; body + footer 1587 nil
1516 (if (= nbody nfooter) 1588 (tbody
1517 `((tr nil (td nil (table 1589 nil
1518 nil (tbody nil ,@body ,@footer))))) 1590 ,@footer))))))))))
1519 (nconc `((tr nil (td nil (table nil (tbody nil ,@body))))) 1591 ;; header + body
1520 (if (= nfooter 1) 1592 (if (= nheader nbody)
1521 footer 1593 `((tr nil (td nil (table nil (tbody nil ,@header
1522 `((tr nil (td nil (table 1594 ,@body)))))
1523 nil (tbody nil ,@footer))))))))) 1595 (if (= nheader 1)
1524 (caption 1596 `(,@header (tr nil (td nil (table
1525 `((tr nil (td nil (table nil (tbody nil ,@body)))))) 1597 nil (tbody nil ,@body)))))
1526 (body))))) 1598 `((tr nil (td nil (table nil (tbody nil ,@header))))
1599 (tr nil (td nil (table nil (tbody nil ,@body)))))))))
1600 (footer
1601 ;; body + footer
1602 (if (= nbody nfooter)
1603 `((tr nil (td nil (table
1604 nil (tbody nil ,@body ,@footer)))))
1605 (nconc `((tr nil (td nil (table nil (tbody nil ,@body)))))
1606 (if (= nfooter 1)
1607 footer
1608 `((tr nil (td nil (table
1609 nil (tbody nil ,@footer)))))))))
1610 (caption
1611 `((tr nil (td nil (table nil (tbody nil ,@body))))))
1612 (body)))))
1613 (dom-set-attribute table 'shr-fixed-table t)
1614 (setcdr dom (cdr table))
1615 (shr-tag-table-1 dom))))
1527 (when bgcolor 1616 (when bgcolor
1528 (shr-colorize-region start (point) (cdr (assq 'color shr-stylesheet)) 1617 (shr-colorize-region start (point) (cdr (assq 'color shr-stylesheet))
1529 bgcolor)) 1618 bgcolor))
@@ -1531,6 +1620,8 @@ The preference is a float determined from `shr-prefer-media-type'."
1531 ;; model isn't strong enough to allow us to put the images actually 1620 ;; model isn't strong enough to allow us to put the images actually
1532 ;; into the tables. 1621 ;; into the tables.
1533 (when (zerop shr-table-depth) 1622 (when (zerop shr-table-depth)
1623 (save-excursion
1624 (shr-expand-alignments start (point)))
1534 (dolist (elem (dom-by-tag dom 'object)) 1625 (dolist (elem (dom-by-tag dom 'object))
1535 (shr-tag-object elem)) 1626 (shr-tag-object elem))
1536 (dolist (elem (dom-by-tag dom 'img)) 1627 (dolist (elem (dom-by-tag dom 'img))
@@ -1540,38 +1631,87 @@ The preference is a float determined from `shr-prefer-media-type'."
1540 (let* ((collapse (equal (cdr (assq 'border-collapse shr-stylesheet)) 1631 (let* ((collapse (equal (cdr (assq 'border-collapse shr-stylesheet))
1541 "collapse")) 1632 "collapse"))
1542 (shr-table-separator-length (if collapse 0 1)) 1633 (shr-table-separator-length (if collapse 0 1))
1543 (shr-table-vertical-line (if collapse "" shr-table-vertical-line))) 1634 (shr-table-vertical-line (if collapse "" shr-table-vertical-line))
1635 (start (point)))
1636 (setq shr-table-id (1+ shr-table-id))
1544 (unless collapse 1637 (unless collapse
1545 (shr-insert-table-ruler widths)) 1638 (shr-insert-table-ruler widths))
1546 (dolist (row table) 1639 (dolist (row table)
1547 (let ((start (point)) 1640 (let ((start (point))
1641 (align 0)
1642 (column-number 0)
1548 (height (let ((max 0)) 1643 (height (let ((max 0))
1549 (dolist (column row) 1644 (dolist (column row)
1550 (setq max (max max (cadr column)))) 1645 (setq max (max max (nth 2 column))))
1551 max))) 1646 max)))
1552 (dotimes (i height) 1647 (dotimes (i (max height 1))
1553 (shr-indent) 1648 (shr-indent)
1554 (insert shr-table-vertical-line "\n")) 1649 (insert shr-table-vertical-line "\n"))
1555 (dolist (column row) 1650 (dolist (column row)
1556 (goto-char start) 1651 (when (> (nth 2 column) -1)
1557 (let ((lines (nth 2 column))) 1652 (goto-char start)
1558 (dolist (line lines) 1653 ;; Sum up all the widths from the column. (There may be
1559 (end-of-line) 1654 ;; more than one if this is a "colspan" column.)
1560 (insert line shr-table-vertical-line) 1655 (dotimes (i (nth 4 column))
1561 (forward-line 1)) 1656 ;; The colspan directive may be wrong and there may not be
1562 ;; Add blank lines at padding at the bottom of the TD, 1657 ;; that number of columns.
1563 ;; possibly. 1658 (when (<= column-number (1- (length widths)))
1564 (dotimes (i (- height (length lines))) 1659 (setq align (+ align
1565 (end-of-line) 1660 (aref widths column-number)
1566 (let ((start (point))) 1661 (* 2 shr-table-separator-pixel-width))))
1567 (insert (make-string (string-width (car lines)) ? ) 1662 (setq column-number (1+ column-number)))
1568 shr-table-vertical-line) 1663 (let ((lines (nth 3 column))
1569 (when (nth 4 column) 1664 (pixel-align (if (not shr-use-fonts)
1570 (shr-add-font start (1- (point)) 1665 (* align (frame-char-width))
1571 (list :background (nth 4 column))))) 1666 align)))
1572 (forward-line 1))))) 1667 (dolist (line lines)
1668 (end-of-line)
1669 (let ((start (point)))
1670 (insert line
1671 (propertize " "
1672 'display `(space :align-to (,pixel-align))
1673 'shr-table-indent shr-table-id)
1674 shr-table-vertical-line)
1675 (shr-colorize-region
1676 start (1- (point)) (nth 5 column) (nth 6 column)))
1677 (forward-line 1))
1678 ;; Add blank lines at padding at the bottom of the TD,
1679 ;; possibly.
1680 (dotimes (i (- height (length lines)))
1681 (end-of-line)
1682 (let ((start (point)))
1683 (insert (propertize " "
1684 'display `(space :align-to (,pixel-align))
1685 'shr-table-indent shr-table-id)
1686 shr-table-vertical-line)
1687 (shr-colorize-region
1688 start (1- (point)) (nth 5 column) (nth 6 column)))
1689 (forward-line 1))))))
1573 (unless collapse 1690 (unless collapse
1574 (shr-insert-table-ruler widths))))) 1691 (shr-insert-table-ruler widths)))
1692 (unless (= start (point))
1693 (put-text-property start (1+ start) 'shr-table-id shr-table-id))))
1694
1695(defun shr-expand-alignments (start end)
1696 (while (< (setq start (next-single-property-change
1697 start 'shr-table-id nil end))
1698 end)
1699 (goto-char start)
1700 (let* ((shr-use-fonts t)
1701 (id (get-text-property (point) 'shr-table-id))
1702 (base (shr-pixel-column))
1703 elem)
1704 (when id
1705 (save-excursion
1706 (while (setq elem (text-property-any
1707 (point) end 'shr-table-indent id))
1708 (goto-char elem)
1709 (let ((align (get-text-property (point) 'display)))
1710 (put-text-property (point) (1+ (point)) 'display
1711 `(space :align-to (,(+ (car (nth 2 align))
1712 base)))))
1713 (forward-char 1)))))
1714 (setq start (1+ start))))
1575 1715
1576(defun shr-insert-table-ruler (widths) 1716(defun shr-insert-table-ruler (widths)
1577 (when shr-table-horizontal-line 1717 (when shr-table-horizontal-line
@@ -1579,9 +1719,17 @@ The preference is a float determined from `shr-prefer-media-type'."
1579 (> shr-indentation 0)) 1719 (> shr-indentation 0))
1580 (shr-indent)) 1720 (shr-indent))
1581 (insert shr-table-corner) 1721 (insert shr-table-corner)
1582 (dotimes (i (length widths)) 1722 (let ((total-width 0))
1583 (insert (make-string (aref widths i) shr-table-horizontal-line) 1723 (dotimes (i (length widths))
1584 shr-table-corner)) 1724 (setq total-width (+ total-width (aref widths i)
1725 (* shr-table-separator-pixel-width 2)))
1726 (insert (make-string (1+ (/ (aref widths i)
1727 shr-table-separator-pixel-width))
1728 shr-table-horizontal-line)
1729 (propertize " "
1730 'display `(space :align-to (,total-width))
1731 'shr-table-indent shr-table-id)
1732 shr-table-corner)))
1585 (insert "\n"))) 1733 (insert "\n")))
1586 1734
1587(defun shr-table-widths (table natural-table suggested-widths) 1735(defun shr-table-widths (table natural-table suggested-widths)
@@ -1599,7 +1747,8 @@ The preference is a float determined from `shr-prefer-media-type'."
1599 (aset natural-widths i (max (aref natural-widths i) column)) 1747 (aset natural-widths i (max (aref natural-widths i) column))
1600 (setq i (1+ i))))) 1748 (setq i (1+ i)))))
1601 (let ((extra (- (apply '+ (append suggested-widths nil)) 1749 (let ((extra (- (apply '+ (append suggested-widths nil))
1602 (apply '+ (append widths nil)))) 1750 (apply '+ (append widths nil))
1751 (* shr-table-separator-pixel-width (length widths))))
1603 (expanded-columns 0)) 1752 (expanded-columns 0))
1604 ;; We have extra, unused space, so divide this space amongst the 1753 ;; We have extra, unused space, so divide this space amongst the
1605 ;; columns. 1754 ;; columns.
@@ -1617,11 +1766,13 @@ The preference is a float determined from `shr-prefer-media-type'."
1617 (aref widths i)))))))) 1766 (aref widths i))))))))
1618 widths)) 1767 widths))
1619 1768
1620(defun shr-make-table (dom widths &optional fill) 1769(defun shr-make-table (dom widths &optional fill storage-attribute)
1621 (or (cadr (assoc (list dom widths fill) shr-content-cache)) 1770 (or (cadr (assoc (list dom widths fill) shr-content-cache))
1622 (let ((data (shr-make-table-1 dom widths fill))) 1771 (let ((data (shr-make-table-1 dom widths fill)))
1623 (push (list (list dom widths fill) data) 1772 (push (list (list dom widths fill) data)
1624 shr-content-cache) 1773 shr-content-cache)
1774 (when storage-attribute
1775 (dom-set-attribute dom storage-attribute data))
1625 data))) 1776 data)))
1626 1777
1627(defun shr-make-table-1 (dom widths &optional fill) 1778(defun shr-make-table-1 (dom widths &optional fill)
@@ -1634,7 +1785,7 @@ The preference is a float determined from `shr-prefer-media-type'."
1634 (dolist (row (dom-non-text-children dom)) 1785 (dolist (row (dom-non-text-children dom))
1635 (when (eq (dom-tag row) 'tr) 1786 (when (eq (dom-tag row) 'tr)
1636 (let ((tds nil) 1787 (let ((tds nil)
1637 (columns (dom-children row)) 1788 (columns (dom-non-text-children row))
1638 (i 0) 1789 (i 0)
1639 (width-column 0) 1790 (width-column 0)
1640 column) 1791 column)
@@ -1660,7 +1811,7 @@ The preference is a float determined from `shr-prefer-media-type'."
1660 (setq width 1811 (setq width
1661 (if column 1812 (if column
1662 (aref widths width-column) 1813 (aref widths width-column)
1663 10)) 1814 (* 10 shr-table-separator-pixel-width)))
1664 (when (setq colspan (dom-attr column 'colspan)) 1815 (when (setq colspan (dom-attr column 'colspan))
1665 (setq colspan (min (string-to-number colspan) 1816 (setq colspan (min (string-to-number colspan)
1666 ;; The colspan may be wrong, so 1817 ;; The colspan may be wrong, so
@@ -1682,35 +1833,80 @@ The preference is a float determined from `shr-prefer-media-type'."
1682 (setq width-column (+ width-column (1- colspan)) 1833 (setq width-column (+ width-column (1- colspan))
1683 colspan-count colspan 1834 colspan-count colspan
1684 colspan-remaining colspan)) 1835 colspan-remaining colspan))
1685 (when (or column 1836 (when column
1686 (not fill))
1687 (let ((data (shr-render-td column width fill))) 1837 (let ((data (shr-render-td column width fill)))
1688 (if (and (not fill) 1838 (if (and (not fill)
1689 (> colspan-remaining 0)) 1839 (> colspan-remaining 0))
1690 (progn 1840 (progn
1691 (when (= colspan-count colspan-remaining) 1841 (setq colspan-width (car data))
1692 (setq colspan-width data))
1693 (let ((this-width (/ colspan-width colspan-count))) 1842 (let ((this-width (/ colspan-width colspan-count)))
1694 (push this-width tds) 1843 (push (cons this-width (cadr data)) tds)
1695 (setq colspan-remaining (1- colspan-remaining)))) 1844 (setq colspan-remaining (1- colspan-remaining))))
1696 (push data tds)))) 1845 (if (not fill)
1846 (push (cons (car data) (cadr data)) tds)
1847 (push data tds)))))
1848 (when (and colspan
1849 (> colspan 1))
1850 (dotimes (c (1- colspan))
1851 (setq i (1+ i))
1852 (push
1853 (if fill
1854 (list 0 0 -1 nil 1 nil nil)
1855 '(0 . 0))
1856 tds)))
1697 (setq i (1+ i) 1857 (setq i (1+ i)
1698 width-column (1+ width-column)))) 1858 width-column (1+ width-column))))
1699 (push (nreverse tds) trs)))) 1859 (push (nreverse tds) trs))))
1700 (nreverse trs))) 1860 (nreverse trs)))
1701 1861
1862(defun shr-pixel-buffer-width ()
1863 (if (not shr-use-fonts)
1864 (save-excursion
1865 (goto-char (point-min))
1866 (let ((max 0))
1867 (while (not (eobp))
1868 (end-of-line)
1869 (setq max (max max (current-column)))
1870 (forward-line 1))
1871 max))
1872 (if (get-buffer-window)
1873 (car (window-text-pixel-size nil (point-min) (point-max)))
1874 (save-window-excursion
1875 (set-window-buffer nil (current-buffer))
1876 (car (window-text-pixel-size nil (point-min) (point-max)))))))
1877
1702(defun shr-render-td (dom width fill) 1878(defun shr-render-td (dom width fill)
1879 (let ((cache (intern (format "shr-td-cache-%s-%s" width fill))))
1880 (or (dom-attr dom cache)
1881 (and fill
1882 (let (result)
1883 (dolist (attr (dom-attributes dom))
1884 (let ((name (symbol-name (car attr))))
1885 (when (string-match "shr-td-cache-\\([0-9]+\\)-nil" name)
1886 (let ((cache-width (string-to-number
1887 (match-string 1 name))))
1888 (when (and (>= cache-width width)
1889 (<= (car (cdr attr)) width))
1890 (setq result (cdr attr)))))))
1891 result))
1892 (let ((result (shr-render-td-1 dom width fill)))
1893 (dom-set-attribute dom cache result)
1894 result))))
1895
1896(defun shr-render-td-1 (dom width fill)
1703 (with-temp-buffer 1897 (with-temp-buffer
1704 (let ((bgcolor (dom-attr dom 'bgcolor)) 1898 (let ((bgcolor (dom-attr dom 'bgcolor))
1705 (fgcolor (dom-attr dom 'fgcolor)) 1899 (fgcolor (dom-attr dom 'fgcolor))
1706 (style (dom-attr dom 'style)) 1900 (style (dom-attr dom 'style))
1707 (shr-stylesheet shr-stylesheet) 1901 (shr-stylesheet shr-stylesheet)
1708 actual-colors) 1902 (max-width 0)
1903 natural-width)
1709 (when style 1904 (when style
1710 (setq style (and (string-match "color" style) 1905 (setq style (and (string-match "color" style)
1711 (shr-parse-style style)))) 1906 (shr-parse-style style))))
1712 (when bgcolor 1907 (when bgcolor
1713 (setq style (nconc (list (cons 'background-color bgcolor)) style))) 1908 (setq style (nconc (list (cons 'background-color bgcolor))
1909 style)))
1714 (when fgcolor 1910 (when fgcolor
1715 (setq style (nconc (list (cons 'color fgcolor)) style))) 1911 (setq style (nconc (list (cons 'color fgcolor)) style)))
1716 (when style 1912 (when style
@@ -1718,6 +1914,22 @@ The preference is a float determined from `shr-prefer-media-type'."
1718 (let ((shr-internal-width width) 1914 (let ((shr-internal-width width)
1719 (shr-indentation 0)) 1915 (shr-indentation 0))
1720 (shr-descend dom)) 1916 (shr-descend dom))
1917 (save-window-excursion
1918 (set-window-buffer nil (current-buffer))
1919 (unless fill
1920 (setq natural-width
1921 (or (dom-attr dom 'shr-td-cache-natural)
1922 (let ((natural (max (shr-pixel-buffer-width)
1923 (shr-dom-max-natural-width dom 0))))
1924 (dom-set-attribute dom 'shr-td-cache-natural natural)
1925 natural))))
1926 (if (and natural-width
1927 (<= natural-width width))
1928 (setq max-width natural-width)
1929 (let ((shr-internal-width width))
1930 (shr-fill-lines (point-min) (point-max))
1931 (setq max-width (shr-pixel-buffer-width)))))
1932 (goto-char (point-max))
1721 ;; Delete padding at the bottom of the TDs. 1933 ;; Delete padding at the bottom of the TDs.
1722 (delete-region 1934 (delete-region
1723 (point) 1935 (point)
@@ -1726,48 +1938,31 @@ The preference is a float determined from `shr-prefer-media-type'."
1726 (end-of-line) 1938 (end-of-line)
1727 (point))) 1939 (point)))
1728 (goto-char (point-min)) 1940 (goto-char (point-min))
1729 (let ((max 0)) 1941 (list max-width
1730 (while (not (eobp)) 1942 natural-width
1731 (end-of-line) 1943 (count-lines (point-min) (point-max))
1732 (setq max (max max (current-column))) 1944 (split-string (buffer-string) "\n")
1733 (forward-line 1)) 1945 (if (dom-attr dom 'colspan)
1734 (when fill 1946 (string-to-number (dom-attr dom 'colspan))
1735 (goto-char (point-min)) 1947 1)
1736 ;; If the buffer is totally empty, then put a single blank 1948 (cdr (assq 'color shr-stylesheet))
1737 ;; line here. 1949 (cdr (assq 'background-color shr-stylesheet))))))
1738 (if (zerop (buffer-size)) 1950
1739 (insert (make-string width ? )) 1951(defun shr-dom-max-natural-width (dom max)
1740 ;; Otherwise, fill the buffer. 1952 (if (eq (dom-tag dom) 'table)
1741 (let ((align (dom-attr dom 'align)) 1953 (max max (or
1742 length) 1954 (loop for line in (dom-attr dom 'shr-suggested-widths)
1743 (while (not (eobp)) 1955 maximize (+
1744 (end-of-line) 1956 shr-table-separator-length
1745 (setq length (- width (current-column))) 1957 (loop for elem in line
1746 (when (> length 0) 1958 summing
1747 (cond 1959 (+ (cdr elem)
1748 ((equal align "right") 1960 (* 2 shr-table-separator-length)))))
1749 (beginning-of-line) 1961 0))
1750 (insert (make-string length ? ))) 1962 (dolist (child (dom-children dom))
1751 ((equal align "center") 1963 (unless (stringp child)
1752 (insert (make-string (/ length 2) ? )) 1964 (setq max (max (shr-dom-max-natural-width child max)))))
1753 (beginning-of-line) 1965 max))
1754 (insert (make-string (- length (/ length 2)) ? )))
1755 (t
1756 (insert (make-string length ? )))))
1757 (forward-line 1))))
1758 (when style
1759 (setq actual-colors
1760 (shr-colorize-region
1761 (point-min) (point-max)
1762 (cdr (assq 'color shr-stylesheet))
1763 (cdr (assq 'background-color shr-stylesheet))))))
1764 (if fill
1765 (list max
1766 (count-lines (point-min) (point-max))
1767 (split-string (buffer-string) "\n")
1768 nil
1769 (car actual-colors))
1770 max)))))
1771 1966
1772(defun shr-buffer-width () 1967(defun shr-buffer-width ()
1773 (goto-char (point-min)) 1968 (goto-char (point-min))
@@ -1788,7 +1983,8 @@ The preference is a float determined from `shr-prefer-media-type'."
1788 (aset widths i (max (truncate (* (aref columns i) 1983 (aset widths i (max (truncate (* (aref columns i)
1789 total-percentage 1984 total-percentage
1790 (- shr-internal-width 1985 (- shr-internal-width
1791 (1+ (length columns))))) 1986 (* (1+ (length columns))
1987 shr-table-separator-pixel-width))))
1792 10))) 1988 10)))
1793 widths)) 1989 widths))
1794 1990
@@ -1798,9 +1994,8 @@ The preference is a float determined from `shr-prefer-media-type'."
1798 (dolist (row (dom-non-text-children dom)) 1994 (dolist (row (dom-non-text-children dom))
1799 (when (eq (dom-tag row) 'tr) 1995 (when (eq (dom-tag row) 'tr)
1800 (let ((i 0)) 1996 (let ((i 0))
1801 (dolist (column (dom-children row)) 1997 (dolist (column (dom-non-text-children row))
1802 (when (and (not (stringp column)) 1998 (when (memq (dom-tag column) '(td th))
1803 (memq (dom-tag column) '(td th)))
1804 (let ((width (dom-attr column 'width))) 1999 (let ((width (dom-attr column 'width)))
1805 (when (and width 2000 (when (and width
1806 (string-match "\\([0-9]+\\)%" width) 2001 (string-match "\\([0-9]+\\)%" width)