20

I use eww to browse the internet and more often than not I do not want images to load, there doesn't seem to be much in terms of customizations built in for eww and none dealing with images.

Currently I have a system setup that allows me to start and stop showing images in eww but only after a page refresh, what I want is a way to toggle images on and off without refreshing the page.

Here is my current method which takes advantage of the internals of shr-put-image which eww uses.

(defvar display-graphic-override nil
  "Used to override `display-graphic-p' behavior.
Should either be nil, or a list where the car is the override.")

(defadvice display-graphic-p (around sometimes-lie activate)
  "Use the override if it is non nil."
  (if display-graphic-override
      (car display-graphic-override)
    ad-do-it))

(defadvice eww-render (around no-images activate)
  "Temporarily lie about supporting graphics"
  (let ((display-graphic-override '(nil)))
    ad-do-it))

I have also tried toggling the 'invisible text property where images are but that doesn't seem to work.

2 Answers 2

13

Background

Eww, as many other packages, uses the display text property to display images. That is why toggling the invisible text property didn’t help. You’re making the text itself invisible but the display property is still being shown.

In fact, this is something so common that the code below should work on any buffer that displays images (not just eww).

Solution

The easiest solution is to just remove the display property from the entire buffer. But that’s an irreversible action, you wouldn’t be able to turn images back on without refreshing the page.

The more robust solution, presented below, removes display properties in the buffer and simultaneously backs-up the image to another (useless) property. When called again, the command moves the backed up images back into the display property.

(defvar-local endless/display-images t)

(defun endless/toggle-image-display ()
  "Toggle images display on current buffer."
  (interactive)
  (setq endless/display-images
        (null endless/display-images))
  (endless/backup-display-property endless/display-images))

This is the guy who does the backup and restore everywhere. Text property code isn’t terribly easy to read, but I think this function is short enough to be self explanatory.

(defun endless/backup-display-property (invert &optional object)
  "Move the 'display property at POS to 'display-backup.
Only applies if display property is an image.
If INVERT is non-nil, move from 'display-backup to 'display
instead.
Optional OBJECT specifies the string or buffer. Nil means current
buffer."
  (let* ((inhibit-read-only t)
         (from (if invert 'display-backup 'display))
         (to (if invert 'display 'display-backup))
         (pos (point-min))
         left prop)
    (while (and pos (/= pos (point-max)))
      (if (get-text-property pos from object)
          (setq left pos)
        (setq left (next-single-property-change pos from object)))
      (if (or (null left) (= left (point-max)))
          (setq pos nil)
        (setq prop (get-text-property left from object))
        (setq pos (or (next-single-property-change left from object)
                      (point-max)))
        (when (eq (car prop) 'image)
          (add-text-properties left pos (list from nil to prop) object))))))

Please to let me know if it works! I only tested it on very simple webpages.

2
  • 1
    Beautiful, it works very well for me. Oct 1, 2014 at 19:06
  • I used it a lot and wonder how can I hide the inline image and show the text by default? Apr 9, 2015 at 7:53
8

As of Emacs 25.1 you can pass an argument to eww-reload to load from the cache rather than the network. I'm using the following:

(defun my/eww-toggle-images ()
  "Toggle whether images are loaded and reload the current page fro cache."
  (interactive)
  (setq-local shr-inhibit-images (not shr-inhibit-images))
  (eww-reload t)
  (message "Images are now %s"
           (if shr-inhibit-images "off" "on")))

(define-key eww-mode-map (kbd "I") #'my/eww-toggle-images)
(define-key eww-link-keymap (kbd "I") #'my/eww-toggle-images)

;; minimal rendering by default
(setq-default shr-inhibit-images t)   ; toggle with `I`
(setq-default shr-use-fonts nil)      ; toggle with `F`

This disables images and proportional fonts by default. Font toggling is bound to F by default, so I'm using I for toggling images. (This replaces a default binding for shr-insert-image, but I've never used that.)

5
  • Any difference between using the defvar shr-inhibit-images and the defcustom shr-blocked-images?
    – Basil
    Feb 6, 2018 at 8:29
  • I don’t think so. inhibit is a boolean and blocked is a regex, so you might use the regex to always block certain URLs while still using inhabit to toggle everything else on and off.
    – glucas
    Feb 6, 2018 at 12:03
  • @glucas Thanks for this! I want to know what eww-link-keymap is; reading *Help* didn't help ;) Could you please explain what it is and why add a key binding to that too?
    – legends2k
    Dec 13, 2019 at 13:55
  • 1
    @legends2k That keymap applies when point is on a link in an eww buffer. That additional binding is probably not actually needed as Emacs will fall back to the mode map there anyway.
    – glucas
    Dec 13, 2019 at 19:09
  • 1
    @legends2k Sorry, I was wrong just now. The additional binding is needed because eww has "I" bound to shr-insert-image in the eww-link-keymap by default, so I'm replacing that binding. If you use a different key that does not conflict with any eww defaults, you would only need to add it to the mode map.
    – glucas
    Dec 13, 2019 at 19:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.