diff options
| author | Lars Ingebrigtsen | 2016-02-20 18:01:52 +1100 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2016-02-20 18:03:37 +1100 |
| commit | 80852f843e69b81618f29cfb9aa4b074946cb3c4 (patch) | |
| tree | 23febac62f733f568afaf9ddec54ce3f7745b760 | |
| parent | ad1951dbfb7e289553c25474efdfa02f83c16e71 (diff) | |
| download | emacs-80852f843e69b81618f29cfb9aa4b074946cb3c4.tar.gz emacs-80852f843e69b81618f29cfb9aa4b074946cb3c4.zip | |
Use placeholder images in shr to avoid text moving around
* lisp/net/shr.el (shr-rescale-image): Pass in width/height
from the HTML.
(shr-tag-img): Ditto.
(shr-string-number): New function.
(shr-make-placeholder-image): Make placeholder images.
(shr-tag-img): Insert them if we have SVG support.
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | lisp/net/shr.el | 111 |
2 files changed, 99 insertions, 19 deletions
| @@ -779,6 +779,13 @@ textual parts of a web page and display only that, leaving menus and | |||
| 779 | the like off the page. | 779 | the like off the page. |
| 780 | 780 | ||
| 781 | --- | 781 | --- |
| 782 | *** Images that are being loaded are now marked with grey | ||
| 783 | "placeholder" images of the size specified by the HTML. They are then | ||
| 784 | replaced by the real images asynchronously, which will also now | ||
| 785 | respect width/height HTML specs (unless they specify widths/heights | ||
| 786 | bigger than the current window). | ||
| 787 | |||
| 788 | --- | ||
| 782 | *** You can now use several eww buffers in parallel by renaming eww | 789 | *** You can now use several eww buffers in parallel by renaming eww |
| 783 | buffers you want to keep separate. | 790 | buffers you want to keep separate. |
| 784 | 791 | ||
diff --git a/lisp/net/shr.el b/lisp/net/shr.el index 46aea79c327..78862b373d4 100644 --- a/lisp/net/shr.el +++ b/lisp/net/shr.el | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | (require 'subr-x) | 36 | (require 'subr-x) |
| 37 | (require 'dom) | 37 | (require 'dom) |
| 38 | (require 'seq) | 38 | (require 'seq) |
| 39 | (require 'svg) | ||
| 39 | 40 | ||
| 40 | (defgroup shr nil | 41 | (defgroup shr nil |
| 41 | "Simple HTML Renderer" | 42 | "Simple HTML Renderer" |
| @@ -963,10 +964,14 @@ element is the data blob and the second element is the content-type." | |||
| 963 | (create-image data 'svg t :ascent 100)) | 964 | (create-image data 'svg t :ascent 100)) |
| 964 | ((eq size 'full) | 965 | ((eq size 'full) |
| 965 | (ignore-errors | 966 | (ignore-errors |
| 966 | (shr-rescale-image data content-type))) | 967 | (shr-rescale-image data content-type |
| 968 | (plist-get flags :width) | ||
| 969 | (plist-get flags :height)))) | ||
| 967 | (t | 970 | (t |
| 968 | (ignore-errors | 971 | (ignore-errors |
| 969 | (shr-rescale-image data content-type)))))) | 972 | (shr-rescale-image data content-type |
| 973 | (plist-get flags :width) | ||
| 974 | (plist-get flags :height))))))) | ||
| 970 | (when image | 975 | (when image |
| 971 | ;; When inserting big-ish pictures, put them at the | 976 | ;; When inserting big-ish pictures, put them at the |
| 972 | ;; beginning of the line. | 977 | ;; beginning of the line. |
| @@ -989,21 +994,37 @@ element is the data blob and the second element is the content-type." | |||
| 989 | image) | 994 | image) |
| 990 | (insert (or alt "")))) | 995 | (insert (or alt "")))) |
| 991 | 996 | ||
| 992 | (defun shr-rescale-image (data &optional content-type) | 997 | (defun shr-rescale-image (data content-type width height) |
| 993 | "Rescale DATA, if too big, to fit the current buffer." | 998 | "Rescale DATA, if too big, to fit the current buffer. |
| 999 | WIDTH and HEIGHT are the sizes given in the HTML data, if any." | ||
| 994 | (if (not (and (fboundp 'imagemagick-types) | 1000 | (if (not (and (fboundp 'imagemagick-types) |
| 995 | (get-buffer-window (current-buffer)))) | 1001 | (get-buffer-window (current-buffer)))) |
| 996 | (create-image data nil t :ascent 100) | 1002 | (create-image data nil t :ascent 100) |
| 997 | (let ((edges (window-inside-pixel-edges | 1003 | (let* ((edges (window-inside-pixel-edges |
| 998 | (get-buffer-window (current-buffer))))) | 1004 | (get-buffer-window (current-buffer)))) |
| 999 | (create-image | 1005 | (max-width (truncate (* shr-max-image-proportion |
| 1000 | data 'imagemagick t | 1006 | (- (nth 2 edges) (nth 0 edges))))) |
| 1001 | :ascent 100 | 1007 | (max-height (truncate (* shr-max-image-proportion |
| 1002 | :max-width (truncate (* shr-max-image-proportion | 1008 | (- (nth 3 edges) (nth 1 edges)))))) |
| 1003 | (- (nth 2 edges) (nth 0 edges)))) | 1009 | (when (or (and width |
| 1004 | :max-height (truncate (* shr-max-image-proportion | 1010 | (> width max-width)) |
| 1005 | (- (nth 3 edges) (nth 1 edges)))) | 1011 | (and height |
| 1006 | :format content-type)))) | 1012 | (> height max-height))) |
| 1013 | (setq width nil | ||
| 1014 | height nil)) | ||
| 1015 | (if (and width height) | ||
| 1016 | (create-image | ||
| 1017 | data 'imagemagick t | ||
| 1018 | :ascent 100 | ||
| 1019 | :width width | ||
| 1020 | :height height | ||
| 1021 | :format content-type) | ||
| 1022 | (create-image | ||
| 1023 | data 'imagemagick t | ||
| 1024 | :ascent 100 | ||
| 1025 | :max-width max-width | ||
| 1026 | :max-height max-height | ||
| 1027 | :format content-type))))) | ||
| 1007 | 1028 | ||
| 1008 | ;; url-cache-extract autoloads url-cache. | 1029 | ;; url-cache-extract autoloads url-cache. |
| 1009 | (declare-function url-cache-create-filename "url-cache" (url)) | 1030 | (declare-function url-cache-create-filename "url-cache" (url)) |
| @@ -1427,6 +1448,8 @@ The preference is a float determined from `shr-prefer-media-type'." | |||
| 1427 | (when (> (current-column) 0) | 1448 | (when (> (current-column) 0) |
| 1428 | (insert "\n")) | 1449 | (insert "\n")) |
| 1429 | (let ((alt (dom-attr dom 'alt)) | 1450 | (let ((alt (dom-attr dom 'alt)) |
| 1451 | (width (shr-string-number (dom-attr dom 'width))) | ||
| 1452 | (height (shr-string-number (dom-attr dom 'height))) | ||
| 1430 | (url (shr-expand-url (or url (dom-attr dom 'src))))) | 1453 | (url (shr-expand-url (or url (dom-attr dom 'src))))) |
| 1431 | (let ((start (point-marker))) | 1454 | (let ((start (point-marker))) |
| 1432 | (when (zerop (length alt)) | 1455 | (when (zerop (length alt)) |
| @@ -1440,7 +1463,8 @@ The preference is a float determined from `shr-prefer-media-type'." | |||
| 1440 | (string-match "\\`data:" url)) | 1463 | (string-match "\\`data:" url)) |
| 1441 | (let ((image (shr-image-from-data (substring url (match-end 0))))) | 1464 | (let ((image (shr-image-from-data (substring url (match-end 0))))) |
| 1442 | (if image | 1465 | (if image |
| 1443 | (funcall shr-put-image-function image alt) | 1466 | (funcall shr-put-image-function image alt |
| 1467 | (list :width width :height height)) | ||
| 1444 | (insert alt)))) | 1468 | (insert alt)))) |
| 1445 | ((and (not shr-inhibit-images) | 1469 | ((and (not shr-inhibit-images) |
| 1446 | (string-match "\\`cid:" url)) | 1470 | (string-match "\\`cid:" url)) |
| @@ -1449,7 +1473,8 @@ The preference is a float determined from `shr-prefer-media-type'." | |||
| 1449 | (if (or (not shr-content-function) | 1473 | (if (or (not shr-content-function) |
| 1450 | (not (setq image (funcall shr-content-function url)))) | 1474 | (not (setq image (funcall shr-content-function url)))) |
| 1451 | (insert alt) | 1475 | (insert alt) |
| 1452 | (funcall shr-put-image-function image alt)))) | 1476 | (funcall shr-put-image-function image alt |
| 1477 | (list :width width :height height))))) | ||
| 1453 | ((or shr-inhibit-images | 1478 | ((or shr-inhibit-images |
| 1454 | (and shr-blocked-images | 1479 | (and shr-blocked-images |
| 1455 | (string-match shr-blocked-images url))) | 1480 | (string-match shr-blocked-images url))) |
| @@ -1457,17 +1482,23 @@ The preference is a float determined from `shr-prefer-media-type'." | |||
| 1457 | (shr-insert alt)) | 1482 | (shr-insert alt)) |
| 1458 | ((and (not shr-ignore-cache) | 1483 | ((and (not shr-ignore-cache) |
| 1459 | (url-is-cached (shr-encode-url url))) | 1484 | (url-is-cached (shr-encode-url url))) |
| 1460 | (funcall shr-put-image-function (shr-get-image-data url) alt)) | 1485 | (funcall shr-put-image-function (shr-get-image-data url) alt |
| 1486 | (list :width width :height height))) | ||
| 1461 | (t | 1487 | (t |
| 1462 | (insert alt " ") | ||
| 1463 | (when (and shr-ignore-cache | 1488 | (when (and shr-ignore-cache |
| 1464 | (url-is-cached (shr-encode-url url))) | 1489 | (url-is-cached (shr-encode-url url))) |
| 1465 | (let ((file (url-cache-create-filename (shr-encode-url url)))) | 1490 | (let ((file (url-cache-create-filename (shr-encode-url url)))) |
| 1466 | (when (file-exists-p file) | 1491 | (when (file-exists-p file) |
| 1467 | (delete-file file)))) | 1492 | (delete-file file)))) |
| 1493 | (when (image-type-available-p 'svg) | ||
| 1494 | (insert-image | ||
| 1495 | (shr-make-placeholder-image dom) | ||
| 1496 | (or alt ""))) | ||
| 1497 | (insert " ") | ||
| 1468 | (url-queue-retrieve | 1498 | (url-queue-retrieve |
| 1469 | (shr-encode-url url) 'shr-image-fetched | 1499 | (shr-encode-url url) 'shr-image-fetched |
| 1470 | (list (current-buffer) start (set-marker (make-marker) (1- (point)))) | 1500 | (list (current-buffer) start (set-marker (make-marker) (1- (point))) |
| 1501 | (list :width width :height height)) | ||
| 1471 | t t))) | 1502 | t t))) |
| 1472 | (when (zerop shr-table-depth) ;; We are not in a table. | 1503 | (when (zerop shr-table-depth) ;; We are not in a table. |
| 1473 | (put-text-property start (point) 'keymap shr-image-map) | 1504 | (put-text-property start (point) 'keymap shr-image-map) |
| @@ -1479,6 +1510,48 @@ The preference is a float determined from `shr-prefer-media-type'." | |||
| 1479 | (shr-fill-text | 1510 | (shr-fill-text |
| 1480 | (or (dom-attr dom 'title) alt)))))))) | 1511 | (or (dom-attr dom 'title) alt)))))))) |
| 1481 | 1512 | ||
| 1513 | (defun shr-string-number (string) | ||
| 1514 | (if (null string) | ||
| 1515 | nil | ||
| 1516 | (setq string (replace-regexp-in-string "[^0-9]" "" string)) | ||
| 1517 | (if (zerop (length string)) | ||
| 1518 | nil | ||
| 1519 | (string-to-number string)))) | ||
| 1520 | |||
| 1521 | (defun shr-make-placeholder-image (dom) | ||
| 1522 | (let* ((edges (and | ||
| 1523 | (get-buffer-window (current-buffer)) | ||
| 1524 | (window-inside-pixel-edges | ||
| 1525 | (get-buffer-window (current-buffer))))) | ||
| 1526 | (scaling (image-compute-scaling-factor image-scaling-factor)) | ||
| 1527 | (width (truncate | ||
| 1528 | (* (or (shr-string-number (dom-attr dom 'width)) 100) | ||
| 1529 | scaling))) | ||
| 1530 | (height (truncate | ||
| 1531 | (* (or (shr-string-number (dom-attr dom 'height)) 100) | ||
| 1532 | scaling))) | ||
| 1533 | (max-width | ||
| 1534 | (and edges | ||
| 1535 | (truncate (* shr-max-image-proportion | ||
| 1536 | (- (nth 2 edges) (nth 0 edges)))))) | ||
| 1537 | (max-height (and edges | ||
| 1538 | (truncate (* shr-max-image-proportion | ||
| 1539 | (- (nth 3 edges) (nth 1 edges)))))) | ||
| 1540 | svg image) | ||
| 1541 | (when (and max-width | ||
| 1542 | (> width max-width)) | ||
| 1543 | (setq height (truncate (* (/ (float max-width) width) height)) | ||
| 1544 | width max-width)) | ||
| 1545 | (when (and max-height | ||
| 1546 | (> height max-height)) | ||
| 1547 | (setq width (truncate (* (/ (float max-height) height) width)) | ||
| 1548 | height max-height)) | ||
| 1549 | (setq svg (svg-create width height)) | ||
| 1550 | (svg-gradient svg "background" 'linear '((0 . "#b0b0b0") (100 . "#808080"))) | ||
| 1551 | (svg-rectangle svg 0 0 width height :gradient "background") | ||
| 1552 | (let ((image (svg-image svg))) | ||
| 1553 | (image-set-property image :ascent 100)))) | ||
| 1554 | |||
| 1482 | (defun shr-tag-pre (dom) | 1555 | (defun shr-tag-pre (dom) |
| 1483 | (let ((shr-folding-mode 'none) | 1556 | (let ((shr-folding-mode 'none) |
| 1484 | (shr-current-font 'default)) | 1557 | (shr-current-font 'default)) |